dolibarr 24.0.1
html.formticket.class.php
Go to the documentation of this file.
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-2025 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2021 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
7 * Copyright (C) 2023-2025 Charlene Benke <charlene.r@patas-monkey.com>
8 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Irvine FLEITH <irvine.fleith@atm-consulting.fr>
10 * Copyright (C) 2026 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
11 * Copyright (C) 2026 Lenin Rivas <lenin.rivas777@gmail.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25 */
26
33require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
34require_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
35require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
36
37if (!class_exists('FormCompany')) {
38 include DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
39}
40
50{
54 public $db;
55
59 public $track_id;
60
64 public $trackid;
65
69 public $fk_user_create;
70
74 public $message;
78 public $topic_title;
79
83 public $action;
84
88 public $withtopic;
92 public $withemail;
93
97 public $withsubstit;
98
102 public $withfile;
106 public $withfilereadonly;
107
111 public $backtopage;
112
116 public $ispublic; // to show information or not into public form
117
121 public $withtitletopic;
125 public $withtopicreadonly;
129 public $withreadid;
130
134 public $withcompany; // to show company drop-down list
138 public $withfromsocid;
142 public $withfromcontactid;
146 public $withnotifytiersatcreate;
150 public $withusercreate; // to show name of creating user in form
154 public $withcreatereadonly;
155
159 public $withextrafields;
160
164 public $withref; // to show ref field
168 public $withcancel;
169
173 public $type_code;
177 public $category_code;
181 public $severity_code;
182
183
187 public $substit = array();
191 public $param = array();
192
196 public $error;
200 public $errors = array();
201
202
208 public function __construct($db)
209 {
210 global $conf;
211
212 $this->db = $db;
213
214 $this->action = 'add';
215
216 $this->withcompany = (int) (!getDolGlobalInt("TICKETS_NO_COMPANY_ON_FORM") && isModEnabled("societe"));
217 $this->withfromsocid = 0;
218 $this->withfromcontactid = 0;
219 $this->withreadid = 0;
220 //$this->withtitletopic='';
221 $this->withnotifytiersatcreate = 0;
222 $this->withusercreate = 1;
223 $this->withcreatereadonly = 1;
224 $this->withemail = 0;
225 $this->withref = 0;
226 $this->withextrafields = 0; // to show extrafields or not
227 //$this->withtopicreadonly=0;
228 }
229
237 public static function checkRequiredFields(array $fields, int &$errors)
238 {
239 global $langs;
240
241 foreach ($fields as $field => $type) {
242 if (!GETPOST($field, $type['check'])) {
243 $errors++;
244 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities($type['langs'])), null, 'errors');
245 }
246 }
247 }
248
260 public function showForm($withdolfichehead = 0, $mode = 'edit', $public = 0, $with_contact = null, $action = '', $object = null)
261 {
262 global $conf, $langs, $user, $hookmanager;
263
264 $permissiontomanage = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('ticket', 'write')) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('ticket', 'manage_advance')));
265
266 // Load translation files required by the page
267 $langs->loadLangs(array('other', 'mails', 'ticket'));
268
269 if ($mode == 'create') {
270 $ref = GETPOSTISSET("ref") ? GETPOST("ref", 'alpha') : '';
271 $type_code = GETPOSTISSET('type_code') ? GETPOST('type_code', 'alpha') : '';
272 $category_code = GETPOSTISSET('category_code') ? GETPOST('category_code', 'alpha') : '';
273 $severity_code = GETPOSTISSET('severity_code') ? GETPOST('severity_code', 'alpha') : '';
274 $subject = GETPOSTISSET('subject') ? GETPOST('subject', 'alpha') : '';
275 $email = GETPOSTISSET('email') ? GETPOST('email', 'alpha') : '';
276 $msg = GETPOSTISSET('message') ? GETPOST('message', 'restricthtml') : '';
277 $projectid = GETPOSTISSET('projectid') ? GETPOSTINT('projectid') : 0;
278 $user_assign = GETPOSTISSET('fk_user_assign') ? GETPOSTINT('fk_user_assign') : $this->fk_user_create;
279 } else {
280 $ref = GETPOSTISSET("ref") ? GETPOST("ref", 'alpha') : $object->ref;
281 $type_code = GETPOSTISSET('type_code') ? GETPOST('type_code', 'alpha') : $object->type_code;
282 $category_code = GETPOSTISSET('category_code') ? GETPOST('category_code', 'alpha') : $object->category_code;
283 $severity_code = GETPOSTISSET('severity_code') ? GETPOST('severity_code', 'alpha') : $object->severity_code;
284 $subject = GETPOSTISSET('subject') ? GETPOST('subject', 'alpha') : $object->subject;
285 $email = GETPOSTISSET('email') ? GETPOST('email', 'alpha') : $object->email_from;
286 $msg = GETPOSTISSET('message') ? GETPOST('message', 'restricthtml') : $object->message;
287 $projectid = GETPOSTISSET('projectid') ? GETPOSTINT('projectid') : (int) $object->fk_project;
288 $user_assign = GETPOSTISSET('fk_user_assign') ? GETPOSTINT('fk_user_assign') : $object->fk_user_assign;
289 }
290
291 $form = new Form($this->db);
292 $formcompany = new FormCompany($this->db);
293 $ticketstatic = new Ticket($this->db);
294
295 $soc = new Societe($this->db);
296 if (!empty($this->withfromsocid) && $this->withfromsocid > 0) {
297 $soc->fetch($this->withfromsocid);
298 }
299
300 $ticketstat = new Ticket($this->db);
301
302 $extrafields = new ExtraFields($this->db);
303 $extrafields->fetch_name_optionals_label($ticketstat->table_element);
304
305 print "\n<!-- Begin form TICKET -->\n";
306
307 if ($withdolfichehead) {
308 print dol_get_fiche_head([], 'card', '', 0, '');
309 }
310
311 print '<form method="POST" '.($withdolfichehead ? '' : 'style="margin-bottom: 30px;" ').'name="ticket" id="form_create_ticket" enctype="multipart/form-data" action="'.(!empty($this->param["returnurl"]) ? $this->param["returnurl"] : $_SERVER['PHP_SELF']).'">';
312
313 print '<input type="hidden" name="token" value="'.newToken().'">';
314 print '<input type="hidden" name="action" value="'.$this->action.'">';
315 if (!empty($object->id)) {
316 print '<input type="hidden" name="id" value="'. $object->id .'">';
317 }
318 print '<input type="hidden" name="trackid" value="'.$this->trackid.'">';
319 foreach ($this->param as $key => $value) {
320 print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
321 }
322 print '<input type="hidden" name="fk_user_create" value="'.$this->fk_user_create.'">';
323
324 print '<table class="border centpercent">';
325
326 // Ref
327 if ($this->withref) {
328 $defaultref = $ticketstat->getDefaultRef();
329
330 if ($mode == 'edit') {
331 $defaultref = (string) $object->ref;
332 }
333 print '<tr><td class="titlefieldcreate"><span class="fieldrequired">'.$langs->trans("Ref").'</span></td><td>';
334 print '<input type="text" name="ref" value="'.dol_escape_htmltag($defaultref).'">';
335 print '</td></tr>';
336 }
337
338 // Title
339 if ($this->withemail) {
340 print '<tr><td class="titlefield"><label for="email"><span class="fieldrequired">'.$langs->trans("Email").'</span></label></td><td>';
341 print '<input class="text minwidth200" id="email" name="email" value="'.$email.'" autofocus>'; // Do not use "required", it breaks button cancel
342 print '</td></tr>';
343
344 if ($with_contact) {
345 // contact search and result
346 $html_contact_search = '';
347 $html_contact_search .= '<tr id="contact_search_line">';
348 $html_contact_search .= '<td class="titlefield">';
349 $html_contact_search .= '<label for="contact"><span class="fieldrequired">' . $langs->trans('Contact') . '</span></label>';
350 $html_contact_search .= '<input type="hidden" id="contact_id" name="contact_id" value="" />';
351 $html_contact_search .= '</td>';
352 $html_contact_search .= '<td id="contact_search_result"></td>';
353 $html_contact_search .= '</tr>';
354 print $html_contact_search;
355 // contact lastname
356 $html_contact_lastname = '';
357 $html_contact_lastname .= '<tr id="contact_lastname_line" class="contact_field"><td class="titlefield"><label for="contact_lastname"><span class="fieldrequired">' . $langs->trans('Lastname') . '</span></label></td><td>';
358 $html_contact_lastname .= '<input type="text" id="contact_lastname" name="contact_lastname" value="' . dolPrintHTMLForAttributeUrl(GETPOSTISSET('contact_lastname') ? GETPOST('contact_lastname', 'alphanohtml') : '') . '" />';
359 $html_contact_lastname .= '</td></tr>';
360 print $html_contact_lastname;
361 // contact firstname
362 $html_contact_firstname = '';
363 $html_contact_firstname .= '<tr id="contact_firstname_line" class="contact_field"><td class="titlefield"><label for="contact_firstname"><span class="fieldrequired">' . $langs->trans('Firstname') . '</span></label></td><td>';
364 $html_contact_firstname .= '<input type="text" id="contact_firstname" name="contact_firstname" value="' . dolPrintHTMLForAttribute(GETPOSTISSET('contact_firstname') ? GETPOST('contact_firstname', 'alphanohtml') : '') . '" />';
365 $html_contact_firstname .= '</td></tr>';
366 print $html_contact_firstname;
367 // company name
368 $html_company_name = '';
369 $html_company_name .= '<tr id="contact_company_name_line" class="contact_field"><td><label for="company_name"><span>' . $langs->trans('Company') . '</span></label></td><td>';
370 $html_company_name .= '<input type="text" id="company_name" name="company_name" value="' . dolPrintHTMLForAttribute(GETPOSTISSET('company_name') ? GETPOST('company_name', 'alphanohtml') : '') . '" />';
371 $html_company_name .= '</td></tr>';
372 print $html_company_name;
373 // contact phone
374 $html_contact_phone = '';
375 $html_contact_phone .= '<tr id="contact_phone_line" class="contact_field"><td><label for="contact_phone"><span>' . $langs->trans('Phone') . '</span></label></td><td>';
376 $html_contact_phone .= '<input type="text" id="contact_phone" name="contact_phone" value="' . dolPrintHTMLForAttribute(GETPOSTISSET('contact_phone') ? GETPOST('contact_phone', 'alphanohtml') : '') . '" />';
377 $html_contact_phone .= '</td></tr>';
378 print $html_contact_phone;
379
380 // search contact form email
381 $langs->load('errors');
382 print '<script nonce="'.getNonce().'" type="text/javascript">
383 jQuery(document).ready(function() {
384 var contact = jQuery.parseJSON("'.dol_escape_js(json_encode($with_contact), 2).'");
385 jQuery("#contact_search_line").hide();
386 if (contact) {
387 if (contact.id > 0) {
388 jQuery("#contact_search_line").show();
389 jQuery("#contact_id").val(contact.id);
390 jQuery("#contact_search_result").html(contact.firstname+" "+contact.lastname);
391 jQuery(".contact_field").hide();
392 } else {
393 jQuery(".contact_field").show();
394 }
395 }
396
397 jQuery("#email").change(function() {
398 jQuery("#contact_search_line").show();
399 jQuery("#contact_search_result").html("'.dol_escape_js($langs->trans('Select2SearchInProgress')).'");
400 jQuery("#contact_id").val("");
401 jQuery("#contact_lastname").val("");
402 jQuery("#contact_firstname").val("");
403 jQuery("#company_name").val("");
404 jQuery("#contact_phone").val("");
405
406 jQuery.getJSON(
407 "'.dol_escape_js(dol_buildpath('/public/ticket/ajax/ajax.php', 1)).'",
408 {
409 action: "getContacts",
410 token: "'.currentToken().'",
411 email: jQuery("#email").val()
412 },
413 function(response) {
414 if (response.error) {
415 jQuery("#contact_search_result").html("<span class=\"error\">"+response.error+"</span>");
416 } else {
417 var contact_list = response.contacts;
418 if (contact_list.length == 1) {
419 var contact = contact_list[0];
420 jQuery("#contact_id").val(contact.id);
421 jQuery("#contact_search_result").html(contact.firstname+" "+contact.lastname);
422 jQuery(".contact_field").hide();
423 } else if (contact_list.length <= 0) {
424 jQuery("#contact_search_line").hide();
425 jQuery(".contact_field").show();
426 }
427 }
428 }
429 ).fail(function(jqxhr, textStatus, error) {
430 var error_msg = "'.dol_escape_js($langs->trans('ErrorAjaxRequestFailed')).'"+" ["+textStatus+"] : "+error;
431 jQuery("#contact_search_result").html("<span class=\"error\">"+error_msg+"</span>");
432 });
433 });
434 });
435 </script>';
436 }
437 }
438
439 // If ticket created from another object
440 $subelement = '';
441 if (isset($this->param['origin']) && $this->param['originid'] > 0) {
442 // Parse element/subelement (ex: project_task)
443 $element = $subelement = $this->param['origin'];
444 $regs = array();
445 if (preg_match('/^([^_]+)_([^_]+)/i', $this->param['origin'], $regs)) {
446 $element = $regs[1];
447 $subelement = $regs[2];
448 }
449
450 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
451 $classname = ucfirst($subelement);
452 $objectsrc = new $classname($this->db);
453 '@phan-var-force CommonObject $objectsrc';
454 $objectsrc->fetch(GETPOSTINT('originid'));
455
456 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
457 $objectsrc->fetch_lines();
458 }
459
460 $objectsrc->fetch_thirdparty();
461 $newclassname = $classname;
462 print '<tr><td>'.$langs->trans($newclassname).'</td><td colspan="2"><input name="'.$subelement.'id" value="'.GETPOST('originid').'" type="hidden" />'.$objectsrc->getNomUrl(1).'</td></tr>';
463 }
464
465 // Type of Ticket
466 print '<tr><td class="titlefield"><span class="fieldrequired"><label for="selecttype_code">'.$langs->trans("TicketTypeRequest").'</span></label></td><td>';
467 $this->selectTypesTickets($type_code, 'type_code', '', 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
468 print '</td></tr>';
469
470 // Group => Category
471 print '<tr><td><span class="fieldrequired"><label for="selectcategory_code">'.$langs->trans("TicketCategory").'</span></label></td><td>';
472 $filter = '';
473 if ($public) {
474 $filter = '(public:=:1)';
475 }
476 $this->selectGroupTickets($category_code, 'category_code', $filter, 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
477 print '</td></tr>';
478
479 // Severity => Priority
480 print '<tr><td><span class="fieldrequired"><label for="selectseverity_code">'.$langs->trans("TicketSeverity").'</span></label></td><td>';
481 $this->selectSeveritiesTickets($severity_code, 'severity_code', '', 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
482 print '</td></tr>';
483
484 if (isModEnabled('knowledgemanagement')) {
485 // KM Articles
486 print '<tr id="KWwithajax" class="hidden"><td></td></tr>';
487 print '<!-- Script to manage change of ticket group -->
488 <script nonce="'.getNonce().'">
489 jQuery(document).ready(function() {
490 function groupticketchange() {
491 console.log("We called groupticketchange, so we try to load list KM linked to event");
492 $("#KWwithajax").html("");
493 idgroupticket = $("#selectcategory_code").val();
494
495 console.log("We have selected id="+idgroupticket);
496
497 if (idgroupticket != "") {
498 $.ajax({ url: \''.DOL_URL_ROOT.'/core/ajax/fetchKnowledgeRecord.php\',
499 data: { action: \'getKnowledgeRecord\', idticketgroup: idgroupticket, token: \''.newToken().'\', lang:\''.$langs->defaultlang.'\', public:'.($public).' },
500 type: \'GET\',
501 success: function(response) {
502 var urllist = \'\';
503 console.log("We received response "+response);
504 if (typeof response == "object") {
505 console.log("response is already type object, no need to parse it");
506 } else {
507 console.log("response is type "+(typeof response));
508 response = JSON.parse(response);
509 }
510 for (key in response) {
511 answer = response[key].answer;
512 urllist += \'<li><a href="#" title="\'+response[key].title+\'" class="button_KMpopup" data-html="\'+answer+\'">\' +response[key].title+\'</a></li>\';
513 }
514 if (urllist != "") {
515 $("#KWwithajax").html(\'<td>'.$langs->trans("KMFoundForTicketGroup").'</td><td><ul>\'+urllist+\'</ul></td>\');
516 $("#KWwithajax").show();
517 $(".button_KMpopup").on("click",function(){
518 console.log("Open popup with jQuery(...).dialog() with KM article")
519 var $dialog = $("<div></div>").html($(this).attr("data-html"))
520 .dialog({
521 autoOpen: false,
522 modal: true,
523 height: (window.innerHeight - 150),
524 width: "80%",
525 title: $(this).attr("title"),
526 });
527 $dialog.dialog("open");
528 console.log($dialog);
529 })
530 }
531 },
532 error : function(output) {
533 console.error("Error on Fetch of KM articles");
534 },
535 });
536 }
537 };
538 $("#selectcategory_code").on("change",function() { groupticketchange(); });
539 if ($("#selectcategory_code").val() != "") {
540 groupticketchange();
541 }
542 });
543 </script>'."\n";
544 }
545
546 // Subject
547 if ($this->withtitletopic) {
548 print '<tr><td><label for="subject"><span class="fieldrequired">'.$langs->trans("Subject").'</span></label></td><td>';
549 // Answer to a ticket : display of the thread title in readonly
550 if ($this->withtopicreadonly) {
551 print $langs->trans('SubjectAnswerToTicket').' '.$this->topic_title;
552 } else {
553 if (isset($this->withreadid) && $this->withreadid > 0) {
554 $subject = $langs->trans('SubjectAnswerToTicket').' '.$this->withreadid.' : '.$this->topic_title;
555 }
556 print '<input class="text minwidth400imp" id="subject" name="subject" value="'.dolPrintHTMLForAttribute($subject).'"'.(empty($this->withemail) ? ' autofocus' : '').' spellcheck="false">';
557 }
558 print '</td></tr>';
559 }
560
561 // Message
562 print '<tr><td><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span></label></td><td>';
563
564 // If public form, display more information
565 $toolbarname = 'dolibarr_notes';
566 if ($this->ispublic) {
567 $toolbarname = 'dolibarr_details'; // TODO Allow image so user can do paste of image into content but we disallow file manager
568 print '<div class="warning hideonsmartphone">'.(getDolGlobalString("TICKET_PUBLIC_TEXT_HELP_MESSAGE", $langs->trans('TicketPublicPleaseBeAccuratelyDescribe'))).'</div>';
569 }
570 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
571 $uselocalbrowser = false;
572 $ckeditorenabledforticket = (getDolGlobalString('FCKEDITOR_ENABLE_TICKET') >= ($this->ispublic ? 2 : 1)); // 0=no, 1=from backoffice only, 2=from backoffice+public (very dangerous)
573 if (!$ckeditorenabledforticket) {
574 $msg = dol_string_nohtmltag($msg, 2);
575 }
576 $doleditor = new DolEditor('message', $msg, '100%', 230, $toolbarname, 'In', true, $uselocalbrowser, $ckeditorenabledforticket, ROWS_8, '90%');
577 $doleditor->Create();
578 print '</td></tr>';
579
580 // Categories
581 if (isModEnabled('category') && !$public) {
582 // Categories
583 print '<tr><td class="wordbreak">'.$langs->trans("Categories").'</td><td>';
584 print $form->selectCategories(Categorie::TYPE_TICKET, 'categories', $object);
585 print "</td></tr>";
586 }
587
588 // Attached files
589 if (!empty($this->withfile)) {
590 // Define list of attached files
591 $listofpaths = array();
592 $listofnames = array();
593 $listofmimes = array();
594 if (!empty($_SESSION["listofpaths"])) {
595 $listofpaths = explode(';', $_SESSION["listofpaths"]);
596 }
597
598 if (!empty($_SESSION["listofnames"])) {
599 $listofnames = explode(';', $_SESSION["listofnames"]);
600 }
601
602 if (!empty($_SESSION["listofmimes"])) {
603 $listofmimes = explode(';', $_SESSION["listofmimes"]);
604 }
605
606 $out = '<tr>';
607 $out .= '<td>';
608 $out .= $langs->trans("LinkedFiles");
609 $out .='</td>';
610 $out .= '<td>';
611 // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
612 $out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
613 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
614 $out .= 'jQuery(document).ready(function () {';
615 $out .= ' jQuery(".removedfile").click(function() {';
616 $out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
617 $out .= ' });';
618 $out .= '})';
619 $out .= '</script>'."\n";
620 if (count($listofpaths)) {
621 foreach ($listofpaths as $key => $val) {
622 $out .= '<div id="attachfile_'.$key.'">';
623 $out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
624 if (!$this->withfilereadonly) {
625 $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.'" />';
626 }
627 $out .= '<br></div>';
628 }
629 }
630 if ($this->withfile == 2) { // Can add other files
631 $maxfilesizearray = getMaxFileSizeArray();
632 $maxmin = $maxfilesizearray['maxmin'];
633 if ($maxmin > 0) {
634 $out .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
635 }
636 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
637 $out .= ' ';
638 $out .= '<input type="submit" class="button smallpaddingimp reposition" id="addfile" name="addfile" value="'.$langs->trans("MailingAddFile").'" />';
639 }
640 $out .= "</td></tr>\n";
641
642 // Improves user experience and prevents human error when creating tickets; files do not load.
643 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
644 jQuery(document).ready(function () {
645 jQuery("#addedfile").on("change", function() {
646 // Dispara el clic automáticamente al seleccionar archivo
647 jQuery("#addfile").click();
648 });
649 // Oculta el botón redundante si JS está activo
650 jQuery("#addfile").hide();
651 });
652 </script>';
653
654 print $out;
655 }
656
657 // User of creation
658 if ($this->withusercreate > 0 && $this->fk_user_create) {
659 print '<tr><td class="titlefield">'.$langs->trans("CreatedBy").'</td><td>';
660 $langs->load("users");
661 $fuser = new User($this->db);
662
663 if ($this->withcreatereadonly) {
664 if ($res = $fuser->fetch($this->fk_user_create)) {
665 print $fuser->getNomUrl(1);
666 }
667 }
668 print ' &nbsp; ';
669 print "</td></tr>\n";
670 }
671
672 // Customer or supplier
673 if ($this->withcompany) {
674 // force company and contact id for external user
675 if (empty($user->socid)) {
676 // Company
677 print '<tr><td class="titlefield">'.$langs->trans("ThirdParty").'</td><td>';
678 $events = array();
679 $events[] = array('method' => 'getContacts', 'url' => dol_buildpath('/core/ajax/contacts.php', 1), 'htmlname' => 'contactid', 'params' => array('add-customer-contact' => 'disabled'));
680 print img_picto('', 'company', 'class="pictofixedwidth"');
681 print $form->select_company($this->withfromsocid, 'socid', '', 1, 1, 0, $events, 0, 'minwidth200');
682 print '</td></tr>';
683 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
684 $htmlname = 'socid';
685 print '<script nonce="'.getNonce().'" type="text/javascript">
686 $(document).ready(function () {
687 jQuery("#'.$htmlname.'").change(function () {
688 var obj = '.json_encode($events).';
689 $.each(obj, function(key,values) {
690 if (values.method.length) {
691 runJsCodeForEvent'.$htmlname.'(values);
692 }
693 });
694 });
695
696 function runJsCodeForEvent'.$htmlname.'(obj) {
697 console.log("Run runJsCodeForEvent'.$htmlname.'");
698 var id = $("#'.$htmlname.'").val();
699 var method = obj.method;
700 var url = obj.url;
701 var htmlname = obj.htmlname;
702 var showempty = obj.showempty;
703 $.getJSON(url,
704 {
705 action: method,
706 id: id,
707 htmlname: htmlname,
708 showempty: showempty
709 },
710 function(response) {
711 $.each(obj.params, function(key,action) {
712 if (key.length) {
713 var num = response.num;
714 if (num > 0) {
715 $("#" + key).removeAttr(action);
716 } else {
717 $("#" + key).attr(action, action);
718 }
719 }
720 });
721 $("select#" + htmlname).html(response.value);
722 if (response.num) {
723 var selecthtml_str = response.value;
724 var selecthtml_dom=$.parseHTML(selecthtml_str);
725 if (typeof(selecthtml_dom[0][0]) !== \'undefined\') {
726 $("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
727 }
728 } else {
729 $("#inputautocomplete"+htmlname).val("");
730 }
731 $("select#" + htmlname).change(); /* Trigger event change */
732 }
733 );
734 }
735 });
736 </script>';
737 }
738 if ($mode == 'create') {
739 // Contact and type
740 print '<tr><td>'.$langs->trans("Contact").'</td><td>';
741 // If no socid, set to -1 to avoid full contacts list
742 $selectedCompany = ($this->withfromsocid > 0) ? $this->withfromsocid : -1;
743 print img_picto('', 'contact', 'class="pictofixedwidth"');
744 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
745 print $form->select_contact($selectedCompany, $this->withfromcontactid, 'contactid', 3, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
746
747 print ' ';
748 $formcompany->selectTypeContact($ticketstatic, '', 'type', 'external', '', 0, 'maginleftonly maxwidth200');
749 print '</td></tr>';
750 }
751 } else {
752 print '<tr><td class="titlefield"><input type="hidden" name="socid" value="'.$user->socid.'"/></td>';
753 print '<td><input type="hidden" name="contactid" value="'.$user->contact_id.'"/></td>';
754 print '<td><input type="hidden" name="type" value="Z"/></td></tr>';
755 }
756
757 // Notify thirdparty at creation
758 if (empty($this->ispublic) && ($action == 'create' || $action == 'presend')) {
759 print '<tr><td><label for="notify_tiers_at_create">'.$langs->trans("TicketNotifyTiersAtCreation").'</label></td><td>';
760 print '<input type="checkbox" id="notify_tiers_at_create" name="notify_tiers_at_create"'.($this->withnotifytiersatcreate ? ' checked="checked"' : '').'>';
761 print '</td></tr>';
762 }
763
764 // User assigned
765 print '<tr><td>';
766 print $langs->trans("AssignedTo");
767 print '</td><td>';
768 if ($permissiontomanage) {
769 print img_picto('', 'user', 'class="pictofixedwidth"');
770 print $form->select_dolusers($user_assign, 'fk_user_assign', 1);
771 } else {
772 $userstat = new User($this->db);
773 $userstat->fetch($user_assign);
774 print $userstat->getNomUrl(-1);
775 }
776 print '</td>';
777 print '</tr>';
778 }
779
780 if ($subelement != 'project') {
781 if (isModEnabled('project') && !$this->ispublic) {
782 $formproject = new FormProjets($this->db);
783 print '<tr><td><label for="project"><span class="">'.$langs->trans("Project").'</span></label></td><td>';
784 print img_picto('', 'project', 'class="pictofixedwidth"').$formproject->select_projects(-1, $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500');
785 print '</td></tr>';
786 }
787 }
788
789 if ($subelement != 'contract' && $subelement != 'contrat') {
790 if (getDolGlobalString('TICKET_LINK_TO_CONTRACT_WITH_HARDLINK')) {
791 // Deprecated. Duplicate feature. Ticket can already be linked to contract with the generic "Link to" feature.
792 if (isModEnabled('contract') && !$this->ispublic) {
793 // This feature hang the application on large list of contracts, because the select component is not complete: it does not work like select of thirdparty or product to support large lists
794 // So we add a hidden option to avoid to have it used and the application locked, until the select_contract is fixed.
795 if (getDolGlobalString("CONTRACT_CAN_USE_THE_BUGGED_SELECT_COMPONENT")) {
796 $langs->load('contracts');
797 $formcontract = new FormContract($this->db);
798 print '<tr><td><label for="contract"><span class="">'.$langs->trans("Contract").'</span></label></td><td>';
799 print img_picto('', 'contract', 'class="pictofixedwidth"');
800 // socid is for internal users null and not 0 or -1
801 print $formcontract->select_contract($user->socid ?? -1, GETPOSTINT('contractid'), 'contractid', 0, 1, 1, 1);
802 print '</td></tr>';
803 }
804 }
805 }
806 }
807
808 // Other attributes
809 $parameters = array();
810 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $ticketstat, $action); // Note that $action and $object may have been modified by hook
811 if (empty($reshook)) {
812 if ($mode == 'create') {
813 print $object->showOptionals($extrafields, 'create');
814 } else {
815 print $object->showOptionals($extrafields, 'edit');
816 }
817 }
818
819 // Show line with Captcha
820 $captcha = '';
821 if ($public && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_TICKET')) {
822 print '<tr><td class="titlefield"></td><td><br>';
823
824 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
825 $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard');
826
827 // List of directories where we can find captcha handlers
828 $dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
829
830 $fullpathclassfile = '';
831 foreach ($dirModCaptcha as $dir) {
832 $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
833 if ($fullpathclassfile) {
834 break;
835 }
836 }
837
838 if ($fullpathclassfile) {
839 include_once $fullpathclassfile;
840 $captchaobj = null;
841
842 // Charging the numbering class
843 $classname = "modCaptcha".ucfirst($captcha);
844 if (class_exists($classname)) {
846 $captchaobj = new $classname($this->db, $conf, $langs, $user);
847 '@phan-var-force ModeleCaptcha $captchaobj';
848
849 if (is_object($captchaobj) && method_exists($captchaobj, 'getCaptchaCodeForForm')) {
850 print $captchaobj->getCaptchaCodeForForm(''); // @phan-suppress-current-line PhanUndeclaredMethod
851 } else {
852 print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method getCaptchaCodeForForm()';
853 }
854 } else {
855 print 'Error, the captcha handler class '.$classname.' was not found after the include';
856 }
857 } else {
858 print 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha);
859 }
860
861 print '<br></td></tr>';
862 }
863
864 print '</table>';
865
866 if ($withdolfichehead) {
867 print dol_get_fiche_end();
868 } else {
869 print '<br><br>';
870 }
871
872 if ($mode == 'create') {
873 print $form->buttonsSaveCancel(((isset($this->withreadid) && $this->withreadid > 0) ? "SendResponse" : "CreateTicket"), ($this->withcancel ? "Cancel" : ""));
874 } else {
875 print $form->buttonsSaveCancel(((isset($this->withreadid) && $this->withreadid > 0) ? "SendResponse" : "Save"), ($this->withcancel ? "Cancel" : ""));
876 }
877
878 print '<br>';
879
880 /*
881 print '<div class="center">';
882 print '<input type="submit" class="button" name="add" value="'.$langs->trans(($this->withreadid > 0 ? "SendResponse" : "CreateTicket")).'" />';
883 if ($this->withcancel) {
884 print " &nbsp; &nbsp; &nbsp;";
885 print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
886 }
887 print '</div>';
888 */
889
890 print '<input type="hidden" name="page_y">'."\n";
891
892 print "</form>\n";
893 print "<!-- End form TICKET -->\n";
894 }
895
911 public function selectTypesTickets($selected = '', $htmlname = 'tickettype', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '', $multiselect = 0, $disabledefault = 0)
912 {
913 global $langs, $user;
914
915 $selected = is_array($selected) ? $selected : (!empty($selected) ? explode(',', $selected) : array());
916 $ticketstat = new Ticket($this->db);
917
918 dol_syslog(get_class($this) . "::select_types_tickets " . implode(';', $selected) . ", " . $htmlname . ", " . $filtertype . ", " . $format . ", " . $multiselect, LOG_DEBUG);
919
920 $filterarray = array();
921
922 if ($filtertype != '' && $filtertype != '-1') {
923 $filterarray = explode(',', $filtertype);
924 }
925
926 $ticketstat->loadCacheTypesTickets();
927
928 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.($multiselect ? '[]' : '').'"'.($multiselect ? ' multiple' : '').'>';
929 if ($empty && !$multiselect) {
930 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
931 }
932
933 if (is_array($ticketstat->cache_types_tickets) && count($ticketstat->cache_types_tickets)) {
934 foreach ($ticketstat->cache_types_tickets as $id => $arraytypes) {
935 // We stop if we astto filter on some ticket type and code is not one requested.
936 if (count($filterarray) && !in_array($arraytypes['code'], $filterarray)) {
937 continue;
938 }
939
940 // If 'showempty' is enabled we discard empty line because an empty line has already been output.
941 if ($empty && empty($arraytypes['code'])) {
942 continue;
943 }
944
945 if ($format == 0) {
946 print '<option value="'.$id.'"';
947 }
948
949 if ($format == 1) {
950 print '<option value="'.$arraytypes['code'].'"';
951 }
952
953 if ($format == 2) {
954 print '<option value="'.$arraytypes['code'].'"';
955 }
956
957 if ($format == 3) {
958 print '<option value="'.$id.'"';
959 }
960
961 // If text is selected, we compare with code, otherwise with id
962 if (in_array($arraytypes['code'], $selected)) {
963 print ' selected="selected"';
964 } elseif (in_array($id, $selected)) {
965 print ' selected="selected"';
966 } elseif ($arraytypes['use_default'] == "1" && empty($disabledefault) && empty($selected) && !$multiselect) {
967 print ' selected="selected"';
968 } elseif (count($ticketstat->cache_types_tickets) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
969 print ' selected="selected"';
970 }
971
972 print '>';
973
974 $value = '&nbsp;';
975 if ($format == 0) {
976 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
977 } elseif ($format == 1) {
978 $value = $arraytypes['code'];
979 } elseif ($format == 2) {
980 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
981 } elseif ($format == 3) {
982 $value = $arraytypes['code'];
983 }
984
985 print $value ? $value : '&nbsp;';
986 print '</option>';
987 }
988 }
989 print '</select>';
990 if (isset($user->admin) && $user->admin && !$noadmininfo) {
991 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
992 }
993
994 print ajax_combobox('select'.$htmlname);
995 }
996
1012 public function selectGroupTickets($selected = '', $htmlname = 'ticketcategory', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '', $use_multilevel = 0, $outputlangs = null)
1013 {
1014 global $conf, $langs, $user;
1015
1016 dol_syslog(get_class($this)."::selectCategoryTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
1017
1018 if (is_null($outputlangs) || !is_object($outputlangs)) {
1019 $outputlangs = $langs;
1020 }
1021 $outputlangs->load("ticket");
1022
1023 $publicgroups = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1024
1025 $ticketstat = new Ticket($this->db);
1026 $ticketstat->loadCacheCategoriesTickets($publicgroups ? 1 : -1); // get list of active ticket groups
1027
1028 if ($use_multilevel <= 0) { // Only one combo list to select the group of ticket (default)
1029 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1030 if ($empty) {
1031 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
1032 }
1033
1034 $categorytickets = $conf->cache['category_tickets'];
1035 '@phan-var-force array<int,array{code:string,label:string,use_default:int,pos:int,public:int,active:int,force_severity:?string,fk_parent:int}> $categorytickets';
1036 if (is_array($categorytickets) && count($categorytickets)) {
1037 foreach ($categorytickets as $id => $arraycategories) {
1038 // Exclude some record
1039 if ($publicgroups) {
1040 if (empty($arraycategories['public'])) {
1041 continue;
1042 }
1043 }
1044
1045 // We discard empty line if showempty is on because an empty line has already been output.
1046 if ($empty && empty($arraycategories['code'])) {
1047 continue;
1048 }
1049
1050 $label = ($arraycategories['label'] != '-' ? $arraycategories['label'] : '');
1051 if ($outputlangs->trans("TicketCategoryShort".$arraycategories['code']) != "TicketCategoryShort".$arraycategories['code']) {
1052 $label = $outputlangs->trans("TicketCategoryShort".$arraycategories['code']);
1053 } elseif ($outputlangs->trans($arraycategories['code']) != $arraycategories['code']) {
1054 $label = $outputlangs->trans($arraycategories['code']);
1055 }
1056
1057 if ($format == 0) {
1058 print '<option value="'.$id.'"';
1059 }
1060
1061 if ($format == 1) {
1062 print '<option value="'.$arraycategories['code'].'"';
1063 }
1064
1065 if ($format == 2) {
1066 print '<option value="'.$arraycategories['code'].'"';
1067 }
1068
1069 if ($format == 3) {
1070 print '<option value="'.$id.'"';
1071 }
1072
1073 // If selected is text, we compare with code, otherwise with id
1074 if (isset($selected) && preg_match('/[a-z]/i', $selected) && $selected == $arraycategories['code']) {
1075 print ' selected="selected"';
1076 } elseif (isset($selected) && $selected == $id) {
1077 print ' selected="selected"';
1078 } elseif ($arraycategories['use_default'] == "1" && empty($selected) && (!$empty || $empty == 'ifone')) {
1079 print ' selected="selected"';
1080 } elseif (count($categorytickets) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
1081 print ' selected="selected"';
1082 }
1083
1084 print '>';
1085
1086 $value = '';
1087 if ($format == 0) {
1088 $value = ($maxlength ? dol_trunc($label, $maxlength) : $label);
1089 }
1090
1091 if ($format == 1) {
1092 $value = $arraycategories['code'];
1093 }
1094
1095 if ($format == 2) {
1096 $value = ($maxlength ? dol_trunc($label, $maxlength) : $label);
1097 }
1098
1099 if ($format == 3) {
1100 $value = $arraycategories['code'];
1101 }
1102
1103 print $value ? $value : '&nbsp;';
1104 print '</option>';
1105 }
1106 }
1107 print '</select>';
1108 if (isset($user->admin) && $user->admin && !$noadmininfo) {
1109 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1110 }
1111
1112 print ajax_combobox('select'.$htmlname);
1113 } elseif ($htmlname != '') { // complexe mode using selection of group using a combo for each level (when using a hierarchy of groups).
1114 $selectedgroups = array();
1115 $groupvalue = "";
1116 $groupticket = GETPOST($htmlname, 'aZ09');
1117 $child_id = GETPOST($htmlname.'_child_id', 'aZ09') ? GETPOST($htmlname.'_child_id', 'aZ09') : 0;
1118 if (!empty($groupticket)) {
1119 $tmpgroupticket = $groupticket;
1120 $sql = "SELECT ctc.rowid, ctc.fk_parent, ctc.code";
1121 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc WHERE ctc.code = '".$this->db->escape($tmpgroupticket)."'";
1122 $resql = $this->db->query($sql);
1123 if ($resql) {
1124 $obj = $this->db->fetch_object($resql);
1125 $selectedgroups[] = $obj->code;
1126 while ($obj->fk_parent > 0) {
1127 $sql = "SELECT ctc.rowid, ctc.fk_parent, ctc.code FROM ".$this->db->prefix()."c_ticket_category as ctc WHERE ctc.rowid ='".$this->db->escape($obj->fk_parent)."'";
1128 $resql = $this->db->query($sql);
1129 if ($resql) {
1130 $obj = $this->db->fetch_object($resql);
1131 $selectedgroups[] = $obj->code;
1132 }
1133 }
1134 }
1135 }
1136
1137 $arrayidused = array();
1138 $arrayidusedconcat = array();
1139 $arraycodenotparent = array();
1140 $arraycodenotparent[] = "";
1141
1142 $stringtoprint = '<span class="supportemailfield bold">'.$langs->trans("GroupOfTicket").'</span> ';
1143 $stringtoprint .= '<select id="'.$htmlname.'" class="minwidth500" child_id="0">';
1144 $stringtoprint .= '<option value="">&nbsp;</option>';
1145
1146 $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctc.public, ";
1147 $sql .= $this->db->ifsql("ctc.rowid NOT IN (SELECT ctcfather.rowid FROM ".MAIN_DB_PREFIX."c_ticket_category as ctcfather JOIN ".MAIN_DB_PREFIX."c_ticket_category as ctcjoin ON ctcfather.rowid = ctcjoin.fk_parent WHERE ctcjoin.active > 0)", "'NOTPARENT'", "'PARENT'")." as isparent";
1148 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc";
1149 $sql .= " WHERE ctc.active > 0 AND ctc.entity = ".((int) $conf->entity);
1150 $public = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1151 if ($public) {
1152 $sql .= " AND ctc.public = 1";
1153 }
1154 $sql .= " AND ctc.fk_parent = 0";
1155 $sql .= $this->db->order('ctc.pos', 'ASC');
1156
1157 $resql = $this->db->query($sql);
1158 if ($resql) {
1159 $num_rows_level0 = $this->db->num_rows($resql);
1160 $i = 0;
1161 while ($i < $num_rows_level0) {
1162 $obj = $this->db->fetch_object($resql);
1163 if ($obj) {
1164 $label = ($obj->label != '-' ? $obj->label : '');
1165 if ($outputlangs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code) {
1166 $label = $outputlangs->trans("TicketCategoryShort".$obj->code);
1167 } elseif ($outputlangs->trans($obj->code) != $obj->code) {
1168 $label = $outputlangs->trans($obj->code);
1169 }
1170
1171 $grouprowid = $obj->rowid;
1172 $groupvalue = $obj->code;
1173 $grouplabel = $label;
1174
1175 $isparent = $obj->isparent;
1176 if (is_array($selectedgroups)) {
1177 $iselected = in_array($obj->code, $selectedgroups) ? 'selected' : '';
1178 } else {
1179 $iselected = $groupticket == $obj->code ? 'selected' : '';
1180 }
1181 $stringtoprint .= '<option '.$iselected.' class="'.$htmlname.dol_escape_htmltag($grouprowid).'" value="'.dol_escape_htmltag($groupvalue).'" data-html="'.dol_escape_htmltag($grouplabel).'">'.dol_escape_htmltag($grouplabel).'</option>';
1182 if ($isparent == 'NOTPARENT') {
1183 $arraycodenotparent[] = $groupvalue;
1184 }
1185 $arrayidused[] = $grouprowid;
1186 $arrayidusedconcat[] = $grouprowid;
1187 }
1188 $i++;
1189 }
1190 } else {
1191 dol_print_error($this->db);
1192 }
1193 if (count($arrayidused) == 1) {
1194 return '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.dol_escape_htmltag($groupvalue).'">';
1195 } else {
1196 $stringtoprint .= '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'_select" class="maxwidth500 minwidth400" value="'.GETPOST($htmlname).'">';
1197 $stringtoprint .= '<input type="hidden" name="'.$htmlname.'_child_id" id="'.$htmlname.'_select_child_id" class="maxwidth500 minwidth400" '.GETPOST($htmlname).' value="'.GETPOST($htmlname."_child_id").'">';
1198 }
1199 $stringtoprint .= '</select>&nbsp;';
1200
1201 $levelid = 1; // The first combobox
1202 while ($levelid <= $use_multilevel) { // Loop to take the child of the combo
1203 $tabscript = array();
1204 $stringtoprint .= '<select id="'.$htmlname.'_child_'.$levelid.'" class="maxwidth500 minwidth400 groupticketchild" child_id="'.$levelid.'">';
1205 $stringtoprint .= '<option value="">&nbsp;</option>';
1206
1207 $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctc.public, ctcjoin.code as codefather";
1208 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc";
1209 $sql .= " JOIN ".$this->db->prefix()."c_ticket_category as ctcjoin ON ctc.fk_parent = ctcjoin.rowid";
1210 $sql .= " WHERE ctc.active > 0 AND ctc.entity = ".((int) $conf->entity);
1211 $sql .= " AND ctc.rowid NOT IN (".$this->db->sanitize(implode(',', $arrayidusedconcat)).")";
1212
1213 $public = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1214 if ($public) {
1215 $sql .= " AND ctc.public = 1";
1216 }
1217 // Add a test to take only record that are direct child
1218 if (!empty($arrayidused)) {
1219 $sql .= " AND ctc.fk_parent IN ( ";
1220 foreach ($arrayidused as $idused) {
1221 $sql .= ((int) $idused).", ";
1222 }
1223 $sql = substr($sql, 0, -2);
1224 $sql .= ")";
1225 }
1226 $sql .= $this->db->order('ctc.pos', 'ASC');
1227
1228 $resql = $this->db->query($sql);
1229 if ($resql) {
1230 $num_rows = $this->db->num_rows($resql);
1231 $i = 0;
1232 $arrayidused = array();
1233 while ($i < $num_rows) {
1234 $obj = $this->db->fetch_object($resql);
1235 if ($obj) {
1236 $label = ($obj->label != '-' ? $obj->label : '');
1237 if ($outputlangs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code) {
1238 $label = $outputlangs->trans("TicketCategoryShort".$obj->code);
1239 } elseif ($outputlangs->trans($obj->code) != $obj->code) {
1240 $label = $outputlangs->trans($obj->code);
1241 }
1242
1243 $grouprowid = $obj->rowid;
1244 $groupvalue = $obj->code;
1245 $grouplabel = $label;
1246 $isparent = $obj->isparent;
1247 $fatherid = $obj->fk_parent;
1248 $arrayidused[] = $grouprowid;
1249 $arrayidusedconcat[] = $grouprowid;
1250 $groupcodefather = $obj->codefather;
1251 if ($isparent == 'NOTPARENT') {
1252 $arraycodenotparent[] = $groupvalue;
1253 }
1254 if (is_array($selectedgroups)) {
1255 $iselected = in_array($obj->code, $selectedgroups) ? 'selected' : '';
1256 } else {
1257 $iselected = $groupticket == $obj->code ? 'selected' : '';
1258 }
1259 $stringtoprint .= '<option '.$iselected.' class="'.$htmlname.'_'.dol_escape_htmltag($fatherid).'_child_'.$levelid.'" value="'.dol_escape_htmltag($groupvalue).'" data-html="'.dol_escape_htmltag($grouplabel).'">'.dol_escape_htmltag($grouplabel).'</option>';
1260 if (empty($tabscript[$groupcodefather])) {
1261 $tabscript[$groupcodefather] = 'if ($("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").val() == "'.dol_escape_js($groupcodefather).'"){
1262 $(".'.$htmlname.'_'.dol_escape_htmltag($fatherid).'_child_'.$levelid.'").show()
1263 console.log("We show child tickets of '.$groupcodefather.' group ticket")
1264 }else{
1265 $(".'.$htmlname.'_'.dol_escape_htmltag($fatherid).'_child_'.$levelid.'").hide()
1266 console.log("We hide child tickets of '.$groupcodefather.' group ticket")
1267 }';
1268 }
1269 }
1270 $i++;
1271 }
1272 } else {
1273 dol_print_error($this->db);
1274 }
1275 $stringtoprint .= '</select>';
1276
1277 $stringtoprint .= '<script nonce="'.getNonce().'">';
1278 $stringtoprint .= 'arraynotparents = '.json_encode($arraycodenotparent).';'; // when the last visible combo list is number x, this is the array of group
1279 $stringtoprint .= 'if (arraynotparents.includes($("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").val())){
1280 console.log("'.$htmlname.'_child_'.$levelid.'")
1281 if($("#'.$htmlname.'_child_'.$levelid.'").val() == "" && ($("#'.$htmlname.'_child_'.$levelid.'").attr("child_id")>'.$child_id.')){
1282 $("#'.$htmlname.'_child_'.$levelid.'").hide();
1283 console.log("We hide '.$htmlname.'_child_'.$levelid.' input")
1284 }
1285 if(arraynotparents.includes("'.$groupticket.'") && '.$child_id.' == 0){
1286 $("#ticketcategory_select_child_id").val($("#'.$htmlname.'").attr("child_id"))
1287 $("#ticketcategory_select").val($("#'.$htmlname.'").val()) ;
1288 console.log("We choose '.$htmlname.' input and reload hidden input");
1289 }
1290 }
1291 $("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").change(function() {
1292 child_id = $("#'.$htmlname.($levelid > 1 ? '_child_'.$levelid : '').'").attr("child_id");
1293
1294 /* Change of value to select this value*/
1295 if (arraynotparents.includes($(this).val()) || $(this).attr("child_id") == '.$use_multilevel.') {
1296 $("#ticketcategory_select").val($(this).val());
1297 $("#ticketcategory_select_child_id").val($(this).attr("child_id")) ;
1298 console.log("We choose to select "+ $(this).val());
1299 }else{
1300 if ($("#'.$htmlname.'_child_'.$levelid.' option").length <= 1) {
1301 $("#ticketcategory_select").val($(this).val());
1302 $("#ticketcategory_select_child_id").val($(this).attr("child_id"));
1303 console.log("We choose to select "+ $(this).val() + " and next combo has no item, so we keep this selection");
1304 } else {
1305 console.log("We choose to select "+ $(this).val() + " but next combo has some item, so we clean selected item");
1306 $("#ticketcategory_select").val("");
1307 $("#ticketcategory_select_child_id").val("");
1308 }
1309 }
1310
1311 console.log("We select a new value into combo child_id="+child_id);
1312
1313 // Hide all selected box that are child of the one modified
1314 $(".groupticketchild").each(function(){
1315 if ($(this).attr("child_id") > child_id) {
1316 console.log("hide child_id="+$(this).attr("child_id"));
1317 $(this).val("");
1318 $(this).hide();
1319 }
1320 })
1321
1322 // Now we enable the next combo
1323 $("#'.$htmlname.'_child_'.$levelid.'").val("");
1324 if (!arraynotparents.includes($(this).val()) && $("#'.$htmlname.'_child_'.$levelid.' option").length > 1) {
1325 console.log($("#'.$htmlname.'_child_'.$levelid.' option").length);
1326 $("#'.$htmlname.'_child_'.$levelid.'").show()
1327 } else {
1328 $("#'.$htmlname.'_child_'.$levelid.'").hide()
1329 }
1330 ';
1331 $levelid++;
1332 foreach ($tabscript as $script) {
1333 $stringtoprint .= $script;
1334 }
1335 $stringtoprint .= '})';
1336 $stringtoprint .= '</script>';
1337 }
1338 $stringtoprint .= '<script nonce="'.getNonce().'">';
1339 $stringtoprint .= '$("#'.$htmlname.'_child_'.$use_multilevel.'").change(function() {
1340 $("#ticketcategory_select").val($(this).val());
1341 $("#ticketcategory_select_child_id").val($(this).attr("child_id"));
1342 tmpvalselect = $("#ticketcategory_select").val();
1343 if(tmpvalselect == "" && $("#ticketcategory_select_child_id").val() >= 1){
1344 $("#ticketcategory_select_child_id").val($(this).attr("child_id")-1);
1345 }
1346 console.log($("#ticketcategory_select").val());
1347 })';
1348 $stringtoprint .= '</script>';
1349 $stringtoprint .= ajax_combobox($htmlname);
1350
1351 return $stringtoprint;
1352 }
1353 }
1354
1368 public function selectSeveritiesTickets($selected = '', $htmlname = 'ticketseverity', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '')
1369 {
1370 global $conf, $langs, $user;
1371
1372 $ticketstat = new Ticket($this->db);
1373
1374 dol_syslog(get_class($this)."::selectSeveritiesTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
1375
1376 $filterarray = array();
1377
1378 if ($filtertype != '' && $filtertype != '-1') {
1379 $filterarray = explode(',', $filtertype);
1380 }
1381
1382 $ticketstat->loadCacheSeveritiesTickets();
1383
1384 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1385 if ($empty) {
1386 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
1387 }
1388
1389 $severitytickets = $conf->cache['severity_tickets'];
1390 '@phan-var-force array<int,array{code:string,label:string,use_default:int,pos:int}> $severitytickets';
1391 if (is_array($severitytickets) && count($severitytickets)) {
1392 foreach ($severitytickets as $id => $arrayseverities) {
1393 // On passe si on a demande de filtrer sur des modes de paiments particuliers
1394 if (count($filterarray) && !in_array($arrayseverities['code'], $filterarray)) {
1395 continue;
1396 }
1397
1398 // We discard empty line if showempty is on because an empty line has already been output.
1399 if ($empty && empty($arrayseverities['code'])) {
1400 continue;
1401 }
1402
1403 if ($format == 0) {
1404 print '<option value="'.$id.'"';
1405 }
1406
1407 if ($format == 1) {
1408 print '<option value="'.$arrayseverities['code'].'"';
1409 }
1410
1411 if ($format == 2) {
1412 print '<option value="'.$arrayseverities['code'].'"';
1413 }
1414
1415 if ($format == 3) {
1416 print '<option value="'.$id.'"';
1417 }
1418
1419 // If text is selected, we compare with code, otherwise with id
1420 if (isset($selected) && preg_match('/[a-z]/i', $selected) && $selected == $arrayseverities['code']) {
1421 print ' selected="selected"';
1422 } elseif (isset($selected) && $selected == $id) {
1423 print ' selected="selected"';
1424 } elseif ($arrayseverities['use_default'] == "1" && empty($selected) && (!$empty || $empty == 'ifone')) {
1425 print ' selected="selected"';
1426 } elseif (count($severitytickets) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
1427 print ' selected="selected"';
1428 }
1429
1430 print '>';
1431
1432 $value = '';
1433 if ($format == 0) {
1434 $value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
1435 }
1436
1437 if ($format == 1) {
1438 $value = $arrayseverities['code'];
1439 }
1440
1441 if ($format == 2) {
1442 $value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
1443 }
1444
1445 if ($format == 3) {
1446 $value = $arrayseverities['code'];
1447 }
1448
1449 print $value ? $value : '&nbsp;';
1450 print '</option>';
1451 }
1452 }
1453 print '</select>';
1454 if (isset($user->admin) && $user->admin && !$noadmininfo) {
1455 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1456 }
1457
1458 print ajax_combobox('select'.$htmlname);
1459 }
1460
1461 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1467 public function clear_attached_files()
1468 {
1469 // phpcs:enable
1470 global $conf, $user;
1471 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1472
1473 // Set tmp user directory
1474 $vardir = $conf->user->dir_output."/".$user->id;
1475 $upload_dir = $vardir.'/temp/'; // TODO Add $keytoavoidconflict in upload_dir path
1476 if (is_dir($upload_dir)) {
1477 dol_delete_dir_recursive($upload_dir);
1478 }
1479
1480 if (!empty($this->trackid)) { // TODO Always use trackid (ticXXX) instead of track_id (abcd123)
1481 $keytoavoidconflict = '-'.$this->trackid;
1482 } else {
1483 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id;
1484 }
1485 unset($_SESSION["listofpaths".$keytoavoidconflict]);
1486 unset($_SESSION["listofnames".$keytoavoidconflict]);
1487 unset($_SESSION["listofmimes".$keytoavoidconflict]);
1488 }
1489
1497 public function showMessageForm($width = '40%', $fromPublicInterface = 0)
1498 {
1499 global $conf, $langs, $user, $hookmanager, $form;
1500
1501 $formmail = new FormMail($this->db);
1502 $addfileaction = 'addfile';
1503
1504 if (!is_object($form)) {
1505 $form = new Form($this->db);
1506 }
1507
1508 // Load translation files required by the page
1509 $langs->loadLangs(array('other', 'mails', 'ticket'));
1510
1511 // Clear temp files. Must be done at beginning, before call of triggers
1512 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
1513 $this->clear_attached_files();
1514 }
1515
1516 // Define output language
1517 $outputlangs = $langs;
1518 $newlang = '';
1519 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->param['langsmodels'])) {
1520 $newlang = $this->param['langsmodels'];
1521 }
1522 if (!empty($newlang)) {
1523 $outputlangs = new Translate("", $conf);
1524 $outputlangs->setDefaultLang($newlang);
1525 $outputlangs->load('other');
1526 }
1527
1528 // Get message template for $this->param["models"] into c_email_templates
1529 $arraydefaultmessage = -1;
1530 if (isset($this->param['models']) && $this->param['models'] != 'none') {
1531 $model_id = 0;
1532 if (array_key_exists('models_id', $this->param)) {
1533 $model_id = (int) $this->param["models_id"];
1534 }
1535
1536 // If $model_id is empty, preselect the first one
1537 $usedefault = ($model_id ? -1 : 1);
1538 $arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id, 1, '', $usedefault);
1539 if (isset($arraydefaultmessage->id) && empty($model_id)) {
1540 $model_id = $arraydefaultmessage->id;
1541 $this->param['models_id'] = $model_id;
1542 }
1543 }
1544
1545 // Define list of attached files
1546 $listofpaths = array();
1547 $listofnames = array();
1548 $listofmimes = array();
1549
1550 if (!empty($this->trackid)) {
1551 $keytoavoidconflict = '-'.$this->trackid;
1552 } else {
1553 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id; // track_id instead of trackid
1554 }
1555 //var_dump($keytoavoidconflict);
1556 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
1557 if (!empty($arraydefaultmessage->joinfiles) && !empty($this->param['fileinit']) && is_array($this->param['fileinit'])) {
1558 foreach ($this->param['fileinit'] as $path) {
1559 $formmail->add_attached_files($path, basename($path), dol_mimetype($path));
1560 }
1561 }
1562 }
1563 //var_dump($_SESSION);
1564 //var_dump($_SESSION["listofpaths".$keytoavoidconflict]);
1565 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
1566 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
1567 }
1568 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
1569 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
1570 }
1571 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
1572 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
1573 }
1574
1575 // Define output language
1576 $outputlangs = $langs;
1577 $newlang = '';
1578 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->param['langsmodels'])) {
1579 $newlang = $this->param['langsmodels'];
1580 }
1581 if (!empty($newlang)) {
1582 $outputlangs = new Translate("", $conf);
1583 $outputlangs->setDefaultLang($newlang);
1584 $outputlangs->load('other');
1585 }
1586
1587 print "\n<!-- Begin message_form TICKET -->\n";
1588
1589 $send_email = GETPOSTINT('send_email') ? GETPOSTINT('send_email') : 0;
1590
1591 // Example 1 : Adding jquery code
1592 print '<script nonce="'.getNonce().'" type="text/javascript">
1593 jQuery(document).ready(function() {
1594 send_email='.((int) $send_email).';
1595 if (send_email) {
1596 if (!jQuery("#send_msg_email").is(":checked")) {
1597 jQuery("#send_msg_email").prop("checked", true).trigger("change");
1598 }
1599 jQuery(".email_line").show();
1600 jQuery(".not_email_line").hide();
1601 } else {
1602 if (!jQuery("#private_message").is(":checked")) {
1603 jQuery("#private_message").prop("checked", true).trigger("change");
1604 }
1605 jQuery(".email_line").hide();
1606 jQuery(".not_email_line").show();
1607 }
1608 ';
1609
1610 // If constant set, allow to send private messages as email
1611 if (!getDolGlobalString('TICKET_SEND_PRIVATE_EMAIL')) {
1612 print 'jQuery("#send_msg_email").click(function() {
1613 console.log("Click send_msg_email");
1614 if(jQuery(this).is(":checked")) {
1615 if (jQuery("#private_message").is(":checked")) {
1616 jQuery("#private_message").prop("checked", false).trigger("change");
1617 }
1618 jQuery(".email_line").show();
1619 jQuery(".not_email_line").hide();
1620 }
1621 else {
1622 jQuery(".email_line").hide();
1623 jQuery(".not_email_line").show();
1624 }
1625 });
1626
1627 jQuery("#private_message").click(function() {
1628 console.log("Click private_message");
1629 if (jQuery(this).is(":checked")) {
1630 if (jQuery("#send_msg_email").is(":checked")) {
1631 jQuery("#send_msg_email").prop("checked", false).trigger("change");
1632 }
1633 jQuery(".email_line").hide();
1634 jQuery(".not_email_line").show();
1635 }
1636 });';
1637 }
1638
1639 print '});
1640 </script>';
1641
1642 print '<form method="post" name="ticket" id="ticket" enctype="multipart/form-data" action="'.$this->param["returnurl"].'">';
1643 print '<input type="hidden" name="token" value="'.newToken().'">';
1644 print '<input type="hidden" name="action" value="'.$this->action.'">';
1645 print '<input type="hidden" name="actionbis" value="add_message">';
1646 print '<input type="hidden" name="backtopage" value="'.$this->backtopage.'">';
1647 if (!empty($this->trackid)) {
1648 print '<input type="hidden" name="trackid" value="'.$this->trackid.'">';
1649 } else {
1650 print '<input type="hidden" name="trackid" value="'.(empty($this->track_id) ? '' : $this->track_id).'">';
1651 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id; // track_id instead of trackid
1652 }
1653 foreach ($this->param as $key => $value) {
1654 print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
1655 }
1656
1657 // Get message template
1658 $model_id = 0;
1659 if (array_key_exists('models_id', $this->param)) {
1660 $model_id = $this->param["models_id"];
1661 $usedefault = ($model_id ? -1 : 1);
1662 $arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id, 1, '', $usedefault);
1663 }
1664
1665 $result = $formmail->fetchAllEMailTemplate(!empty($this->param["models"]) ? $this->param["models"] : "", $user, $outputlangs);
1666 if ($result < 0) {
1667 setEventMessages($this->error, $this->errors, 'errors');
1668 }
1669 $modelmail_array = array();
1670 foreach ($formmail->lines_model as $line) {
1671 $modelmail_array[$line->id] = $line->label;
1672 }
1673
1674 $ticketstat = new Ticket($this->db);
1675 $res = $ticketstat->fetch(0, '', $this->track_id);
1676
1677 print '<table class="border" width="'.$width.'">';
1678
1679 // External users can't send message email
1680 if ($user->hasRight("ticket", "write") && !$user->socid) {
1681 print '<tr><td class="width200"></td><td>';
1682 $checkbox_selected = (GETPOST('send_email') == "1" ? ' checked' : (getDolGlobalInt('TICKETS_MESSAGE_FORCE_MAIL') ? 'checked' : ''));
1683 print '<input type="checkbox" name="send_email" value="1" id="send_msg_email" '.$checkbox_selected.'/> ';
1684 print '<label for="send_msg_email">'.$langs->trans('SendMessageByEmail').'</label>';
1685 $texttooltip = $langs->trans("TicketMessageSendEmailHelp");
1686 if (!getDolGlobalString('TICKET_SEND_PRIVATE_EMAIL')) {
1687 $texttooltip .= ' '.$langs->trans("TicketMessageSendEmailHelp2b");
1688 } else {
1689 $texttooltip .= ' '.$langs->trans("TicketMessageSendEmailHelp2a", '{s1}');
1690 }
1691 $texttooltip = str_replace('{s1}', $langs->trans('MarkMessageAsPrivate'), $texttooltip);
1692 print ' '.$form->textwithpicto('', $texttooltip, 1, 'help');
1693
1694 // Section to selection email template
1695 if (count($modelmail_array) > 0) {
1696 print ' &nbsp; <span class="email_line">';
1697 print $formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], $langs->trans('SelectMailModel'), 0, 0, "", 0, 0, 0, '', 'minwidth200');
1698 if ($user->admin) {
1699 print info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv("Tools").' - '.$langs->transnoentitiesnoconv("EMailTemplates")), 1);
1700 }
1701 print ' &nbsp; ';
1702 print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
1703 print '</span>';
1704 }
1705
1706 print '</td></tr>';
1707
1708 // Private message (not visible by customer/external user)
1709 if (!$user->socid) {
1710 print '<tr><td></td><td>';
1711 $checkbox_selected = (GETPOST('private_message', 'alpha') == "1" ? ' checked' : '');
1712 print '<input type="checkbox" name="private_message" value="1" id="private_message" '.$checkbox_selected.'/> ';
1713 print '<label for="private_message">'.$langs->trans('MarkMessageAsPrivate').'</label>';
1714 print ' '.$form->textwithpicto('', $langs->trans("TicketMessagePrivateHelp"), 1, 'help');
1715 print '</td></tr>';
1716 }
1717
1718 // Zone to select its email template
1719 /*
1720 if (count($modelmail_array) > 0) {
1721 print '<tr class="email_line"><td></td><td colspan="2"><div style="padding: 3px 0 3px 0">'."\n";
1722 print $formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], $langs->trans('SelectMailModel'), 0, 0, "", 0, 0, 0, '', 'minwidth200');
1723 if ($user->admin) {
1724 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1725 }
1726 print ' &nbsp; ';
1727 print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
1728 print '</div></td>';
1729 }
1730 */
1731
1732 // From (and Reply-To if defined)
1733 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
1734 $replyto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_REPLYTO');
1735 print '<tr class="email_line"><td class="width200"><span class="">'.$langs->trans("MailFrom");
1736 if ($replyto) {
1737 print ' <span class="opacitymedium">('.$langs->trans("MailReply").')</span>';
1738 }
1739 print '</span></td>';
1740 print '<td><span class="">'.img_picto('', 'email', 'class="pictofixedwidth"').$from;
1741 if ($replyto) {
1742 print ' <span class="opacitymedium">('.$replyto.')</span>';
1743 }
1744 print '</span></td></tr>';
1745
1746 // Recipients / adressed-to
1747 $parameters = array();
1748 $action = '';
1749 $reshook = $hookmanager->executeHooks('printFieldTicketEmailTo', $parameters, $this, $action);
1750 if (empty($reshook)) {
1751 print '<tr class="email_line"><td>'.$langs->trans('MailRecipients');
1752 print ' '.$form->textwithpicto('', $langs->trans("TicketMessageRecipientsHelp"), 1, 'help');
1753 print '</td><td>';
1754 if ($res) {
1755 // Retrieve email of all contacts (internal and external)
1756 $contacts = $ticketstat->getInfosTicketInternalContact(1);
1757 $contacts = array_merge($contacts, $ticketstat->getInfosTicketExternalContact(1));
1758
1759 $sendto = array();
1760
1761 // Build array to display recipient list
1762 if (is_array($contacts) && count($contacts) > 0) {
1763 foreach ($contacts as $key => $info_sendto) {
1764 if ($info_sendto['email'] != '') {
1765 $sendto[] = dol_escape_htmltag(trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">").' <small class="opacitymedium">('.dol_escape_htmltag($info_sendto['libelle']).")</small>";
1766 }
1767 }
1768 }
1769
1770 if (!empty($ticketstat->origin_replyto) && !in_array($ticketstat->origin_replyto, $sendto)) {
1771 $sendto[] = dol_escape_htmltag((string) $ticketstat->origin_replyto).' <small class="opacitymedium">('.$langs->trans("TicketEmailOriginIssuer").")</small>";
1772 } elseif ($ticketstat->origin_email && !in_array($ticketstat->origin_email, $sendto)) {
1773 $sendto[] = dol_escape_htmltag((string) $ticketstat->origin_email).' <small class="opacitymedium">('.$langs->trans("TicketEmailOriginIssuer").")</small>";
1774 }
1775
1776 if ($ticketstat->fk_soc > 0) {
1777 $ticketstat->socid = $ticketstat->fk_soc;
1778 $ticketstat->fetch_thirdparty();
1779
1780 if (!empty($ticketstat->thirdparty->email) && !in_array($ticketstat->thirdparty->email, $sendto)) {
1781 $sendto[] = $ticketstat->thirdparty->email.' <small class="opacitymedium">('.$langs->trans('Customer').')</small>';
1782 }
1783 }
1784
1785 if (getDolGlobalInt('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS')) {
1786 $sendto[] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO').' <small class="opacitymedium">(generic email)</small>';
1787 }
1788
1789 // Print recipient list
1790 if (is_array($sendto) && count($sendto) > 0) {
1791 print img_picto('', 'email', 'class="pictofixedwidth"');
1792 print implode(', ', $sendto);
1793 } else {
1794 print '<div class="warning">'.$langs->trans('WarningNoEMailsAdded').' '.$langs->trans('TicketGoIntoContactTab').'</div>';
1795 }
1796 }
1797 print '</td></tr>';
1798 } else {
1799 print $hookmanager->resPrint;
1800 }
1801 // Send to CC
1802 $sendtocc = getDolGlobalString('TICKET_SEND_INTERNAL_CC');
1803 if ($sendtocc) {
1804 print '<tr class="email_line"><td><span class="">'.$langs->trans("MailCC").'</span></td>';
1805 print '<td><span class="">'.img_picto('', 'email', 'class="pictofixedwidth"').$sendtocc.'</span></td></tr>';
1806 }
1807 }
1808
1809 $uselocalbrowser = false;
1810
1811 // Summary
1812 if ($this->withtitletopic) {
1813 print '<tr class="not_email_line"><td><label for="summary"><span>'.$langs->trans("Summary").'</span></label></td><td>';
1814 // Answer to a ticket : display of the thread title in readonly
1815 if ($this->withtopicreadonly && $this->topic_title) {
1816 print $langs->trans('Summary').' '.$this->topic_title;
1817 } elseif (empty($this->withtopicreadonly)) {
1818 $subject = $this->topic_title;
1819 print '<input class="text minwidth500" maxlength="42" id="summary" name="summary" placeholder="" value="'.dolPrintHTMLForAttribute($subject).'"'.(empty($this->withemail) ? ' autofocus' : '').' />';
1820 }
1821 print '</td></tr>';
1822 }
1823
1824 $topic = "";
1825 foreach ($formmail->lines_model as $line) {
1826 if (!empty($this->substit) && $this->param['models_id'] == $line->id) {
1827 $topic = make_substitutions($line->topic, $this->substit);
1828 break;
1829 }
1830 }
1831 print '<tr class="email_line"><td class="fieldrequired">'.$langs->trans('MailTopic').'</td>';
1832 if (empty($topic)) {
1833 print '<td><input type="text" class="text minwidth500" name="subject" value="['.getDolGlobalString('MAIN_INFO_SOCIETE_NOM').' - '.$langs->trans("Ticket").' '.$ticketstat->ref.'] '. $ticketstat->subject .'" spellcheck="false">';
1834 } else {
1835 print '<td><input type="text" class="text minwidth500" name="subject" value="'.make_substitutions($topic, $this->substit).'" spellcheck="false">';
1836 }
1837 print '</td></tr>';
1838
1839 // Attached files
1840 if (!empty($this->withfile)) {
1841 $out = '<tr>';
1842 $out .= '<td>'.$langs->trans("MailFile").'</td>';
1843 $out .= '<td>';
1844 // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
1845 $out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
1846 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
1847 $out .= 'jQuery(document).ready(function () {';
1848 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", true);';
1849 $out .= ' jQuery("#addedfile").on("change", function() {';
1850 $out .= ' if (jQuery(this).val().length) {';
1851 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", false);';
1852 $out .= ' } else {';
1853 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", true);';
1854 $out .= ' }';
1855 $out .= ' });';
1856 $out .= ' jQuery(".removedfile").click(function() {';
1857 $out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
1858 $out .= ' });';
1859 $out .= '})';
1860 $out .= '</script>'."\n";
1861
1862 if (count($listofpaths)) {
1863 foreach ($listofpaths as $key => $val) {
1864 $out .= '<div id="attachfile_'.$key.'">';
1865 $out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
1866 if (!$this->withfilereadonly) {
1867 $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.'" />';
1868 }
1869 $out .= '<br></div>';
1870 }
1871 } else {
1872 //$out .= $langs->trans("NoAttachedFiles").'<br>';
1873 }
1874 if ($this->withfile == 2) { // Can add other files
1875 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
1876 $out .= ' ';
1877 $out .= '<input type="submit" class="button smallpaddingimp reposition" id="'.$addfileaction.'" name="'.$addfileaction.'" value="'.$langs->trans("MailingAddFile").'" />';
1878 }
1879 $out .= "</td></tr>\n";
1880
1881 print $out;
1882 }
1883
1884 // MESSAGE
1885 $defaultmessage = "";
1886 if (is_object($arraydefaultmessage) && $arraydefaultmessage->content) {
1887 $defaultmessage = (string) $arraydefaultmessage->content;
1888 }
1889 $defaultmessage = str_replace('\n', "\n", $defaultmessage);
1890
1891 // Deal with format differences between message and signature (text / HTML)
1892 if (dol_textishtml($defaultmessage) && !dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1893 $this->substit['__USER_SIGNATURE__'] = dol_nl2br($this->substit['__USER_SIGNATURE__']);
1894 } elseif (!dol_textishtml($defaultmessage) && isset($this->substit['__USER_SIGNATURE__']) && dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1895 $defaultmessage = dol_nl2br($defaultmessage);
1896 }
1897 if (GETPOSTISSET("message") && !GETPOST('modelselected')) {
1898 $defaultmessage = GETPOST('message', 'restricthtml');
1899 } else {
1900 $defaultmessage = make_substitutions($defaultmessage, $this->substit);
1901 // Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty)
1902 $defaultmessage = preg_replace("/^(<br>)+/", "", $defaultmessage);
1903 $defaultmessage = preg_replace("/^\n+/", "", $defaultmessage);
1904 }
1905
1906 $ckeditorenabledforticket = (getDolGlobalString('FCKEDITOR_ENABLE_TICKET') >= ($fromPublicInterface ? 2 : 1)); // 0=no, 1=from backoffice only, 2=from backoffice+public (very dangerous)
1907
1908 print '<!-- Message line from showMessageForm -->';
1909 print '<tr><td class="tdtop"><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span>';
1910 if ($user->hasRight("ticket", "write") && !$user->socid) {
1911 $texttooltip = $langs->trans("TicketMessageHelp");
1912 if (getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO') || getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE')) {
1913 $texttooltip .= '<br><br>'.$langs->trans("ForEmailMessageWillBeCompletedWith").'...';
1914 }
1915 $allowedmailtags = array('a', 'div', 'strong', 'em', 'i', 'u', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img');
1916 if (getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO')) {
1917 $mail_intro = make_substitutions(getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO'), $this->substit);
1918 print '<input type="hidden" name="mail_intro" value="'.dolPrintHTMLForAttribute($mail_intro, 0, $allowedmailtags).'">';
1919 $texttooltip .= '<br><u>'.$langs->trans("TicketMessageMailIntro").'</u><br>'.$mail_intro;
1920 }
1921 if (getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE')) {
1922 $mail_signature = make_substitutions(getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE'), $this->substit);
1923 print '<input type="hidden" name="mail_signature" value="'.dolPrintHTMLForAttribute($mail_signature, 0, $allowedmailtags).'">';
1924 $texttooltip .= '<br><br><u>'.$langs->trans("TicketMessageMailFooter").'</u><br>'.$mail_signature;
1925 }
1926 print $form->textwithpicto('', $texttooltip, 1, 'help');
1927 }
1928 print '</label></td>';
1929 $out = '<td class="tdtop">';
1930
1931 $out .= '<div class="div-layoutai">';
1932
1933 // Required to show editor assistants
1934 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
1935 $formmail = new FormMail($this->db);
1936
1937 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formai.class.php';
1938 $formai = new FormAI($this->db);
1939
1940 $formmail->withfckeditor = $ckeditorenabledforticket ? 1 : 0;
1941 $formmail->withlayout = ((string) $ckeditorenabledforticket && !$fromPublicInterface) ? 'email' : '';
1942 $formmail->withaiprompt = (isModEnabled('ai') && !$fromPublicInterface) ? 'text' : '';
1943
1944 $showlinktolayout = ($formmail->withfckeditor && getDolGlobalInt('MAIN_EMAIL_USE_LAYOUT')) ? $formmail->withlayout : '';
1945 $showlinktolayoutlabel = $langs->trans("FillMessageWithALayout");
1946 $showlinktoai = ($formmail->withaiprompt ? 'textgenerationemail' : '');
1947 $showlinktoailabel = $langs->trans("AIEnhancements");
1948 $formatforouput = '';
1949 $htmlname = 'message';
1950
1951 $formai->substit = $this->substit;
1952
1953 // Fill $out
1954 $db = $this->db;
1955 include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
1956
1957 $out .= '</td>';
1958 print $out;
1959
1960 print '</tr>';
1961
1962
1963 print '<tr><td colspan="2">';
1964 //$toolbarname = 'dolibarr_details';
1965 $toolbarname = 'dolibarr_notes';
1966 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1967 $uselocalbrowser = false;
1968 if (!$ckeditorenabledforticket) {
1969 $defaultmessage = dol_string_nohtmltag($defaultmessage, 2);
1970 }
1971
1972 $doleditor = new DolEditor('message', $defaultmessage, '100%', 200, $toolbarname, '', false, $uselocalbrowser, $ckeditorenabledforticket, ROWS_6, '90%');
1973 $doleditor->Create();
1974
1975 print '</td></tr>';
1976
1977 // Footer
1978 // External users can't send message email
1979 /*if ($user->rights->ticket->write && !$user->socid && !empty($conf->global->TICKET_MESSAGE_MAIL_SIGNATURE)) {
1980 $mail_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;
1981 print '<tr class="email_line"><td><label for="mail_intro">'.$langs->trans("TicketMessageMailFooter").'</label>';
1982 print $form->textwithpicto('', $langs->trans("TicketMessageMailFooterHelp"), 1, 'help');
1983 print '</td><td>';
1984 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1985 $doleditor = new DolEditor('mail_signature', $mail_signature, '100%', 90, 'dolibarr_details', '', false, $uselocalbrowser, getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_2, 70);
1986 $doleditor->Create();
1987 print '</td></tr>';
1988 }
1989 */
1990
1991 print '</table>';
1992
1993 print '<br><center>';
1994 print '<input type="submit" class="button" name="btn_add_message" value="'.$langs->trans("Add").'"';
1995 // Add a javascript test to avoid to forget to submit file before sending email
1996 if ($this->withfile == 2 && !empty($conf->use_javascript_ajax)) {
1997 print ' onClick="if (document.ticket.addedfile.value != \'\') { alert(\''.dol_escape_js($langs->trans("FileWasNotUploaded")).'\'); return false; } else { return true; }"';
1998 }
1999 print ' />';
2000 if (!empty($this->withcancel)) {
2001 print " &nbsp; &nbsp; ";
2002 print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
2003 }
2004 print "</center>\n";
2005
2006 print '<input type="hidden" name="page_y">'."\n";
2007
2008 print "</form><br>\n";
2009
2010 // Disable enter key if option MAIN_MAILFORM_DISABLE_ENTERKEY is set
2011 if (getDolGlobalString('MAIN_MAILFORM_DISABLE_ENTERKEY')) {
2012 print '<script type="text/javascript">';
2013 print 'jQuery(document).ready(function () {';
2014 print ' $(document).on("keypress", \'#ticket\', function (e) { /* Note this is called at every key pressed ! */
2015 var code = e.keyCode || e.which;
2016 if (code == 13) {
2017 console.log("Enter was intercepted and blocked");
2018 e.preventDefault();
2019 return false;
2020 }
2021 });';
2022 print '})';
2023 print '</script>';
2024 }
2025
2026 print "<!-- End message_form TICKET -->\n";
2027 }
2028}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
$object ref
Definition info.php:90
Class to manage a WYSIWYG editor.
Class to manage standard extra fields.
Class to generate HTML forms for single email Usage: $formai = new FormAI($db) $formai->proprietes=1 ...
Class to build HTML component for third parties management Only common components are here.
Class to manage generation of HTML components for contract module.
Class to manage generation of HTML components Only common components must be here.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class to manage building of HTML components.
selectGroupTickets($selected='', $htmlname='ticketcategory', $filtertype='', $format=0, $empty=0, $noadmininfo=0, $maxlength=0, $morecss='', $use_multilevel=0, $outputlangs=null)
Return html list of ticket analytic codes.
selectSeveritiesTickets($selected='', $htmlname='ticketseverity', $filtertype='', $format=0, $empty=0, $noadmininfo=0, $maxlength=0, $morecss='')
Return html list of ticket severitys (priorities)
selectTypesTickets($selected='', $htmlname='tickettype', $filtertype='', $format=0, $empty=0, $noadmininfo=0, $maxlength=0, $morecss='', $multiselect=0, $disabledefault=0)
Return html list of tickets type.
static checkRequiredFields(array $fields, int &$errors)
Check required fields.
showMessageForm($width='40%', $fromPublicInterface=0)
Show the form to add message on ticket.
__construct($db)
Constructor.
clear_attached_files()
Clear list of attached files in send mail form (also stored in session)
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
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.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
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.
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_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dolPrintHTMLForAttributeUrl($s)
Return a string ready to be output on a href attribute (this one need a special because we need conte...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
Class to generate the form for creating a new ticket.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
getMaxFileSizeArray()
Return the max allowed for file upload.