dolibarr 24.0.0-beta
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 email: jQuery("#email").val()
411 },
412 function(response) {
413 if (response.error) {
414 jQuery("#contact_search_result").html("<span class=\"error\">"+response.error+"</span>");
415 } else {
416 var contact_list = response.contacts;
417 if (contact_list.length == 1) {
418 var contact = contact_list[0];
419 jQuery("#contact_id").val(contact.id);
420 jQuery("#contact_search_result").html(contact.firstname+" "+contact.lastname);
421 jQuery(".contact_field").hide();
422 } else if (contact_list.length <= 0) {
423 jQuery("#contact_search_line").hide();
424 jQuery(".contact_field").show();
425 }
426 }
427 }
428 ).fail(function(jqxhr, textStatus, error) {
429 var error_msg = "'.dol_escape_js($langs->trans('ErrorAjaxRequestFailed')).'"+" ["+textStatus+"] : "+error;
430 jQuery("#contact_search_result").html("<span class=\"error\">"+error_msg+"</span>");
431 });
432 });
433 });
434 </script>';
435 }
436 }
437
438 // If ticket created from another object
439 $subelement = '';
440 if (isset($this->param['origin']) && $this->param['originid'] > 0) {
441 // Parse element/subelement (ex: project_task)
442 $element = $subelement = $this->param['origin'];
443 $regs = array();
444 if (preg_match('/^([^_]+)_([^_]+)/i', $this->param['origin'], $regs)) {
445 $element = $regs[1];
446 $subelement = $regs[2];
447 }
448
449 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
450 $classname = ucfirst($subelement);
451 $objectsrc = new $classname($this->db);
452 '@phan-var-force CommonObject $objectsrc';
453 $objectsrc->fetch(GETPOSTINT('originid'));
454
455 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
456 $objectsrc->fetch_lines();
457 }
458
459 $objectsrc->fetch_thirdparty();
460 $newclassname = $classname;
461 print '<tr><td>'.$langs->trans($newclassname).'</td><td colspan="2"><input name="'.$subelement.'id" value="'.GETPOST('originid').'" type="hidden" />'.$objectsrc->getNomUrl(1).'</td></tr>';
462 }
463
464 // Type of Ticket
465 print '<tr><td class="titlefield"><span class="fieldrequired"><label for="selecttype_code">'.$langs->trans("TicketTypeRequest").'</span></label></td><td>';
466 $this->selectTypesTickets($type_code, 'type_code', '', 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
467 print '</td></tr>';
468
469 // Group => Category
470 print '<tr><td><span class="fieldrequired"><label for="selectcategory_code">'.$langs->trans("TicketCategory").'</span></label></td><td>';
471 $filter = '';
472 if ($public) {
473 $filter = '(public:=:1)';
474 }
475 $this->selectGroupTickets($category_code, 'category_code', $filter, 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
476 print '</td></tr>';
477
478 // Severity => Priority
479 print '<tr><td><span class="fieldrequired"><label for="selectseverity_code">'.$langs->trans("TicketSeverity").'</span></label></td><td>';
480 $this->selectSeveritiesTickets($severity_code, 'severity_code', '', 2, 'ifone', 0, 0, 'minwidth200 maxwidth500');
481 print '</td></tr>';
482
483 if (isModEnabled('knowledgemanagement')) {
484 // KM Articles
485 print '<tr id="KWwithajax" class="hidden"><td></td></tr>';
486 print '<!-- Script to manage change of ticket group -->
487 <script nonce="'.getNonce().'">
488 jQuery(document).ready(function() {
489 function groupticketchange() {
490 console.log("We called groupticketchange, so we try to load list KM linked to event");
491 $("#KWwithajax").html("");
492 idgroupticket = $("#selectcategory_code").val();
493
494 console.log("We have selected id="+idgroupticket);
495
496 if (idgroupticket != "") {
497 $.ajax({ url: \''.DOL_URL_ROOT.'/core/ajax/fetchKnowledgeRecord.php\',
498 data: { action: \'getKnowledgeRecord\', idticketgroup: idgroupticket, token: \''.newToken().'\', lang:\''.$langs->defaultlang.'\', public:'.($public).' },
499 type: \'GET\',
500 success: function(response) {
501 var urllist = \'\';
502 console.log("We received response "+response);
503 if (typeof response == "object") {
504 console.log("response is already type object, no need to parse it");
505 } else {
506 console.log("response is type "+(typeof response));
507 response = JSON.parse(response);
508 }
509 for (key in response) {
510 answer = response[key].answer;
511 urllist += \'<li><a href="#" title="\'+response[key].title+\'" class="button_KMpopup" data-html="\'+answer+\'">\' +response[key].title+\'</a></li>\';
512 }
513 if (urllist != "") {
514 $("#KWwithajax").html(\'<td>'.$langs->trans("KMFoundForTicketGroup").'</td><td><ul>\'+urllist+\'</ul></td>\');
515 $("#KWwithajax").show();
516 $(".button_KMpopup").on("click",function(){
517 console.log("Open popup with jQuery(...).dialog() with KM article")
518 var $dialog = $("<div></div>").html($(this).attr("data-html"))
519 .dialog({
520 autoOpen: false,
521 modal: true,
522 height: (window.innerHeight - 150),
523 width: "80%",
524 title: $(this).attr("title"),
525 });
526 $dialog.dialog("open");
527 console.log($dialog);
528 })
529 }
530 },
531 error : function(output) {
532 console.error("Error on Fetch of KM articles");
533 },
534 });
535 }
536 };
537 $("#selectcategory_code").on("change",function() { groupticketchange(); });
538 if ($("#selectcategory_code").val() != "") {
539 groupticketchange();
540 }
541 });
542 </script>'."\n";
543 }
544
545 // Subject
546 if ($this->withtitletopic) {
547 print '<tr><td><label for="subject"><span class="fieldrequired">'.$langs->trans("Subject").'</span></label></td><td>';
548 // Answer to a ticket : display of the thread title in readonly
549 if ($this->withtopicreadonly) {
550 print $langs->trans('SubjectAnswerToTicket').' '.$this->topic_title;
551 } else {
552 if (isset($this->withreadid) && $this->withreadid > 0) {
553 $subject = $langs->trans('SubjectAnswerToTicket').' '.$this->withreadid.' : '.$this->topic_title;
554 }
555 print '<input class="text minwidth400imp" id="subject" name="subject" value="'.dolPrintHTMLForAttribute($subject).'"'.(empty($this->withemail) ? ' autofocus' : '').' spellcheck="false">';
556 }
557 print '</td></tr>';
558 }
559
560 // Message
561 print '<tr><td><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span></label></td><td>';
562
563 // If public form, display more information
564 $toolbarname = 'dolibarr_notes';
565 if ($this->ispublic) {
566 $toolbarname = 'dolibarr_details'; // TODO Allow image so user can do paste of image into content but we disallow file manager
567 print '<div class="warning hideonsmartphone">'.(getDolGlobalString("TICKET_PUBLIC_TEXT_HELP_MESSAGE", $langs->trans('TicketPublicPleaseBeAccuratelyDescribe'))).'</div>';
568 }
569 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
570 $uselocalbrowser = false;
571 $ckeditorenabledforticket = (getDolGlobalString('FCKEDITOR_ENABLE_TICKET') >= ($this->ispublic ? 2 : 1)); // 0=no, 1=from backoffice only, 2=from backoffice+public (very dangerous)
572 if (!$ckeditorenabledforticket) {
573 $msg = dol_string_nohtmltag($msg, 2);
574 }
575 $doleditor = new DolEditor('message', $msg, '100%', 230, $toolbarname, 'In', true, $uselocalbrowser, $ckeditorenabledforticket, ROWS_8, '90%');
576 $doleditor->Create();
577 print '</td></tr>';
578
579 // Categories
580 if (isModEnabled('category') && !$public) {
581 // Categories
582 print '<tr><td class="wordbreak"></td><td>';
583 print $form->selectCategories(Categorie::TYPE_TICKET, 'categories', $object);
584 print "</td></tr>";
585 }
586
587 // Attached files
588 if (!empty($this->withfile)) {
589 // Define list of attached files
590 $listofpaths = array();
591 $listofnames = array();
592 $listofmimes = array();
593 if (!empty($_SESSION["listofpaths"])) {
594 $listofpaths = explode(';', $_SESSION["listofpaths"]);
595 }
596
597 if (!empty($_SESSION["listofnames"])) {
598 $listofnames = explode(';', $_SESSION["listofnames"]);
599 }
600
601 if (!empty($_SESSION["listofmimes"])) {
602 $listofmimes = explode(';', $_SESSION["listofmimes"]);
603 }
604
605 $out = '<tr>';
606 $out .= '<td></td>';
607 $out .= '<td>';
608 // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
609 $out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
610 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
611 $out .= 'jQuery(document).ready(function () {';
612 $out .= ' jQuery(".removedfile").click(function() {';
613 $out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
614 $out .= ' });';
615 $out .= '})';
616 $out .= '</script>'."\n";
617 if (count($listofpaths)) {
618 foreach ($listofpaths as $key => $val) {
619 $out .= '<div id="attachfile_'.$key.'">';
620 $out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
621 if (!$this->withfilereadonly) {
622 $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.'" />';
623 }
624 $out .= '<br></div>';
625 }
626 }
627 if ($this->withfile == 2) { // Can add other files
628 $maxfilesizearray = getMaxFileSizeArray();
629 $maxmin = $maxfilesizearray['maxmin'];
630 if ($maxmin > 0) {
631 $out .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
632 }
633 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
634 $out .= ' ';
635 $out .= '<input type="submit" class="button smallpaddingimp reposition" id="addfile" name="addfile" value="'.$langs->trans("MailingAddFile").'" />';
636 }
637 $out .= "</td></tr>\n";
638
639 // Improves user experience and prevents human error when creating tickets; files do not load.
640 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
641 jQuery(document).ready(function () {
642 jQuery("#addedfile").on("change", function() {
643 // Dispara el clic automáticamente al seleccionar archivo
644 jQuery("#addfile").click();
645 });
646 // Oculta el botón redundante si JS está activo
647 jQuery("#addfile").hide();
648 });
649 </script>';
650
651 print $out;
652 }
653
654 // User of creation
655 if ($this->withusercreate > 0 && $this->fk_user_create) {
656 print '<tr><td class="titlefield">'.$langs->trans("CreatedBy").'</td><td>';
657 $langs->load("users");
658 $fuser = new User($this->db);
659
660 if ($this->withcreatereadonly) {
661 if ($res = $fuser->fetch($this->fk_user_create)) {
662 print $fuser->getNomUrl(1);
663 }
664 }
665 print ' &nbsp; ';
666 print "</td></tr>\n";
667 }
668
669 // Customer or supplier
670 if ($this->withcompany) {
671 // force company and contact id for external user
672 if (empty($user->socid)) {
673 // Company
674 print '<tr><td class="titlefield">'.$langs->trans("ThirdParty").'</td><td>';
675 $events = array();
676 $events[] = array('method' => 'getContacts', 'url' => dol_buildpath('/core/ajax/contacts.php', 1), 'htmlname' => 'contactid', 'params' => array('add-customer-contact' => 'disabled'));
677 print img_picto('', 'company', 'class="paddingright"');
678 print $form->select_company($this->withfromsocid, 'socid', '', 1, 1, 0, $events, 0, 'minwidth200');
679 print '</td></tr>';
680 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
681 $htmlname = 'socid';
682 print '<script nonce="'.getNonce().'" type="text/javascript">
683 $(document).ready(function () {
684 jQuery("#'.$htmlname.'").change(function () {
685 var obj = '.json_encode($events).';
686 $.each(obj, function(key,values) {
687 if (values.method.length) {
688 runJsCodeForEvent'.$htmlname.'(values);
689 }
690 });
691 });
692
693 function runJsCodeForEvent'.$htmlname.'(obj) {
694 console.log("Run runJsCodeForEvent'.$htmlname.'");
695 var id = $("#'.$htmlname.'").val();
696 var method = obj.method;
697 var url = obj.url;
698 var htmlname = obj.htmlname;
699 var showempty = obj.showempty;
700 $.getJSON(url,
701 {
702 action: method,
703 id: id,
704 htmlname: htmlname,
705 showempty: showempty
706 },
707 function(response) {
708 $.each(obj.params, function(key,action) {
709 if (key.length) {
710 var num = response.num;
711 if (num > 0) {
712 $("#" + key).removeAttr(action);
713 } else {
714 $("#" + key).attr(action, action);
715 }
716 }
717 });
718 $("select#" + htmlname).html(response.value);
719 if (response.num) {
720 var selecthtml_str = response.value;
721 var selecthtml_dom=$.parseHTML(selecthtml_str);
722 if (typeof(selecthtml_dom[0][0]) !== \'undefined\') {
723 $("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
724 }
725 } else {
726 $("#inputautocomplete"+htmlname).val("");
727 }
728 $("select#" + htmlname).change(); /* Trigger event change */
729 }
730 );
731 }
732 });
733 </script>';
734 }
735 if ($mode == 'create') {
736 // Contact and type
737 print '<tr><td>'.$langs->trans("Contact").'</td><td>';
738 // If no socid, set to -1 to avoid full contacts list
739 $selectedCompany = ($this->withfromsocid > 0) ? $this->withfromsocid : -1;
740 print img_picto('', 'contact', 'class="pictofixedwidth"');
741 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
742 print $form->select_contact($selectedCompany, $this->withfromcontactid, 'contactid', 3, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
743
744 print ' ';
745 $formcompany->selectTypeContact($ticketstatic, '', 'type', 'external', '', 0, 'maginleftonly maxwidth200');
746 print '</td></tr>';
747 }
748 } else {
749 print '<tr><td class="titlefield"><input type="hidden" name="socid" value="'.$user->socid.'"/></td>';
750 print '<td><input type="hidden" name="contactid" value="'.$user->contact_id.'"/></td>';
751 print '<td><input type="hidden" name="type" value="Z"/></td></tr>';
752 }
753
754 // Notify thirdparty at creation
755 if (empty($this->ispublic) && ($action == 'create' || $action == 'presend')) {
756 print '<tr><td><label for="notify_tiers_at_create">'.$langs->trans("TicketNotifyTiersAtCreation").'</label></td><td>';
757 print '<input type="checkbox" id="notify_tiers_at_create" name="notify_tiers_at_create"'.($this->withnotifytiersatcreate ? ' checked="checked"' : '').'>';
758 print '</td></tr>';
759 }
760
761 // User assigned
762 print '<tr><td>';
763 print $langs->trans("AssignedTo");
764 print '</td><td>';
765 if ($permissiontomanage) {
766 print img_picto('', 'user', 'class="pictofixedwidth"');
767 print $form->select_dolusers($user_assign, 'fk_user_assign', 1);
768 } else {
769 $userstat = new User($this->db);
770 $userstat->fetch($user_assign);
771 print $userstat->getNomUrl(-1);
772 }
773 print '</td>';
774 print '</tr>';
775 }
776
777 if ($subelement != 'project') {
778 if (isModEnabled('project') && !$this->ispublic) {
779 $formproject = new FormProjets($this->db);
780 print '<tr><td><label for="project"><span class="">'.$langs->trans("Project").'</span></label></td><td>';
781 print img_picto('', 'project', 'class="pictofixedwidth"').$formproject->select_projects(-1, $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500');
782 print '</td></tr>';
783 }
784 }
785
786 if ($subelement != 'contract' && $subelement != 'contrat') {
787 if (getDolGlobalString('TICKET_LINK_TO_CONTRACT_WITH_HARDLINK')) {
788 // Deprecated. Duplicate feature. Ticket can already be linked to contract with the generic "Link to" feature.
789 if (isModEnabled('contract') && !$this->ispublic) {
790 // 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
791 // So we add a hidden option to avoid to have it used and the application locked, until the select_contract is fixed.
792 if (getDolGlobalString("CONTRACT_CAN_USE_THE_BUGGED_SELECT_COMPONENT")) {
793 $langs->load('contracts');
794 $formcontract = new FormContract($this->db);
795 print '<tr><td><label for="contract"><span class="">'.$langs->trans("Contract").'</span></label></td><td>';
796 print img_picto('', 'contract', 'class="pictofixedwidth"');
797 // socid is for internal users null and not 0 or -1
798 print $formcontract->select_contract($user->socid ?? -1, GETPOSTINT('contractid'), 'contractid', 0, 1, 1, 1);
799 print '</td></tr>';
800 }
801 }
802 }
803 }
804
805 // Other attributes
806 $parameters = array();
807 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $ticketstat, $action); // Note that $action and $object may have been modified by hook
808 if (empty($reshook)) {
809 if ($mode == 'create') {
810 print $object->showOptionals($extrafields, 'create');
811 } else {
812 print $object->showOptionals($extrafields, 'edit');
813 }
814 }
815
816 // Show line with Captcha
817 $captcha = '';
818 if ($public && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_TICKET')) {
819 print '<tr><td class="titlefield"></td><td><br>';
820
821 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
822 $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard');
823
824 // List of directories where we can find captcha handlers
825 $dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
826
827 $fullpathclassfile = '';
828 foreach ($dirModCaptcha as $dir) {
829 $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
830 if ($fullpathclassfile) {
831 break;
832 }
833 }
834
835 if ($fullpathclassfile) {
836 include_once $fullpathclassfile;
837 $captchaobj = null;
838
839 // Charging the numbering class
840 $classname = "modCaptcha".ucfirst($captcha);
841 if (class_exists($classname)) {
843 $captchaobj = new $classname($this->db, $conf, $langs, $user);
844 '@phan-var-force ModeleCaptcha $captchaobj';
845
846 if (is_object($captchaobj) && method_exists($captchaobj, 'getCaptchaCodeForForm')) {
847 print $captchaobj->getCaptchaCodeForForm(''); // @phan-suppress-current-line PhanUndeclaredMethod
848 } else {
849 print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method getCaptchaCodeForForm()';
850 }
851 } else {
852 print 'Error, the captcha handler class '.$classname.' was not found after the include';
853 }
854 } else {
855 print 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha);
856 }
857
858 print '<br></td></tr>';
859 }
860
861 print '</table>';
862
863 if ($withdolfichehead) {
864 print dol_get_fiche_end();
865 } else {
866 print '<br><br>';
867 }
868
869 if ($mode == 'create') {
870 print $form->buttonsSaveCancel(((isset($this->withreadid) && $this->withreadid > 0) ? "SendResponse" : "CreateTicket"), ($this->withcancel ? "Cancel" : ""));
871 } else {
872 print $form->buttonsSaveCancel(((isset($this->withreadid) && $this->withreadid > 0) ? "SendResponse" : "Save"), ($this->withcancel ? "Cancel" : ""));
873 }
874
875 print '<br>';
876
877 /*
878 print '<div class="center">';
879 print '<input type="submit" class="button" name="add" value="'.$langs->trans(($this->withreadid > 0 ? "SendResponse" : "CreateTicket")).'" />';
880 if ($this->withcancel) {
881 print " &nbsp; &nbsp; &nbsp;";
882 print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
883 }
884 print '</div>';
885 */
886
887 print '<input type="hidden" name="page_y">'."\n";
888
889 print "</form>\n";
890 print "<!-- End form TICKET -->\n";
891 }
892
908 public function selectTypesTickets($selected = '', $htmlname = 'tickettype', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '', $multiselect = 0, $disabledefault = 0)
909 {
910 global $langs, $user;
911
912 $selected = is_array($selected) ? $selected : (!empty($selected) ? explode(',', $selected) : array());
913 $ticketstat = new Ticket($this->db);
914
915 dol_syslog(get_class($this) . "::select_types_tickets " . implode(';', $selected) . ", " . $htmlname . ", " . $filtertype . ", " . $format . ", " . $multiselect, LOG_DEBUG);
916
917 $filterarray = array();
918
919 if ($filtertype != '' && $filtertype != '-1') {
920 $filterarray = explode(',', $filtertype);
921 }
922
923 $ticketstat->loadCacheTypesTickets();
924
925 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.($multiselect ? '[]' : '').'"'.($multiselect ? ' multiple' : '').'>';
926 if ($empty && !$multiselect) {
927 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
928 }
929
930 if (is_array($ticketstat->cache_types_tickets) && count($ticketstat->cache_types_tickets)) {
931 foreach ($ticketstat->cache_types_tickets as $id => $arraytypes) {
932 // We stop if we astto filter on some ticket type and code is not one requested.
933 if (count($filterarray) && !in_array($arraytypes['code'], $filterarray)) {
934 continue;
935 }
936
937 // If 'showempty' is enabled we discard empty line because an empty line has already been output.
938 if ($empty && empty($arraytypes['code'])) {
939 continue;
940 }
941
942 if ($format == 0) {
943 print '<option value="'.$id.'"';
944 }
945
946 if ($format == 1) {
947 print '<option value="'.$arraytypes['code'].'"';
948 }
949
950 if ($format == 2) {
951 print '<option value="'.$arraytypes['code'].'"';
952 }
953
954 if ($format == 3) {
955 print '<option value="'.$id.'"';
956 }
957
958 // If text is selected, we compare with code, otherwise with id
959 if (in_array($arraytypes['code'], $selected)) {
960 print ' selected="selected"';
961 } elseif (in_array($id, $selected)) {
962 print ' selected="selected"';
963 } elseif ($arraytypes['use_default'] == "1" && empty($disabledefault) && empty($selected) && !$multiselect) {
964 print ' selected="selected"';
965 } elseif (count($ticketstat->cache_types_tickets) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
966 print ' selected="selected"';
967 }
968
969 print '>';
970
971 $value = '&nbsp;';
972 if ($format == 0) {
973 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
974 } elseif ($format == 1) {
975 $value = $arraytypes['code'];
976 } elseif ($format == 2) {
977 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
978 } elseif ($format == 3) {
979 $value = $arraytypes['code'];
980 }
981
982 print $value ? $value : '&nbsp;';
983 print '</option>';
984 }
985 }
986 print '</select>';
987 if (isset($user->admin) && $user->admin && !$noadmininfo) {
988 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
989 }
990
991 print ajax_combobox('select'.$htmlname);
992 }
993
1009 public function selectGroupTickets($selected = '', $htmlname = 'ticketcategory', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '', $use_multilevel = 0, $outputlangs = null)
1010 {
1011 global $conf, $langs, $user;
1012
1013 dol_syslog(get_class($this)."::selectCategoryTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
1014
1015 if (is_null($outputlangs) || !is_object($outputlangs)) {
1016 $outputlangs = $langs;
1017 }
1018 $outputlangs->load("ticket");
1019
1020 $publicgroups = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1021
1022 $ticketstat = new Ticket($this->db);
1023 $ticketstat->loadCacheCategoriesTickets($publicgroups ? 1 : -1); // get list of active ticket groups
1024
1025 if ($use_multilevel <= 0) { // Only one combo list to select the group of ticket (default)
1026 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1027 if ($empty) {
1028 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
1029 }
1030
1031 if (is_array($conf->cache['category_tickets']) && count($conf->cache['category_tickets'])) {
1032 foreach ($conf->cache['category_tickets'] as $id => $arraycategories) {
1033 // Exclude some record
1034 if ($publicgroups) {
1035 if (empty($arraycategories['public'])) {
1036 continue;
1037 }
1038 }
1039
1040 // We discard empty line if showempty is on because an empty line has already been output.
1041 if ($empty && empty($arraycategories['code'])) {
1042 continue;
1043 }
1044
1045 $label = ($arraycategories['label'] != '-' ? $arraycategories['label'] : '');
1046 if ($outputlangs->trans("TicketCategoryShort".$arraycategories['code']) != "TicketCategoryShort".$arraycategories['code']) {
1047 $label = $outputlangs->trans("TicketCategoryShort".$arraycategories['code']);
1048 } elseif ($outputlangs->trans($arraycategories['code']) != $arraycategories['code']) {
1049 $label = $outputlangs->trans($arraycategories['code']);
1050 }
1051
1052 if ($format == 0) {
1053 print '<option value="'.$id.'"';
1054 }
1055
1056 if ($format == 1) {
1057 print '<option value="'.$arraycategories['code'].'"';
1058 }
1059
1060 if ($format == 2) {
1061 print '<option value="'.$arraycategories['code'].'"';
1062 }
1063
1064 if ($format == 3) {
1065 print '<option value="'.$id.'"';
1066 }
1067
1068 // If selected is text, we compare with code, otherwise with id
1069 if (isset($selected) && preg_match('/[a-z]/i', $selected) && $selected == $arraycategories['code']) {
1070 print ' selected="selected"';
1071 } elseif (isset($selected) && $selected == $id) {
1072 print ' selected="selected"';
1073 } elseif ($arraycategories['use_default'] == "1" && empty($selected) && (!$empty || $empty == 'ifone')) {
1074 print ' selected="selected"';
1075 } elseif (count($conf->cache['category_tickets']) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
1076 print ' selected="selected"';
1077 }
1078
1079 print '>';
1080
1081 $value = '';
1082 if ($format == 0) {
1083 $value = ($maxlength ? dol_trunc($label, $maxlength) : $label);
1084 }
1085
1086 if ($format == 1) {
1087 $value = $arraycategories['code'];
1088 }
1089
1090 if ($format == 2) {
1091 $value = ($maxlength ? dol_trunc($label, $maxlength) : $label);
1092 }
1093
1094 if ($format == 3) {
1095 $value = $arraycategories['code'];
1096 }
1097
1098 print $value ? $value : '&nbsp;';
1099 print '</option>';
1100 }
1101 }
1102 print '</select>';
1103 if (isset($user->admin) && $user->admin && !$noadmininfo) {
1104 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1105 }
1106
1107 print ajax_combobox('select'.$htmlname);
1108 } elseif ($htmlname != '') { // complexe mode using selection of group using a combo for each level (when using a hierarchy of groups).
1109 $selectedgroups = array();
1110 $groupvalue = "";
1111 $groupticket = GETPOST($htmlname, 'aZ09');
1112 $child_id = GETPOST($htmlname.'_child_id', 'aZ09') ? GETPOST($htmlname.'_child_id', 'aZ09') : 0;
1113 if (!empty($groupticket)) {
1114 $tmpgroupticket = $groupticket;
1115 $sql = "SELECT ctc.rowid, ctc.fk_parent, ctc.code";
1116 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc WHERE ctc.code = '".$this->db->escape($tmpgroupticket)."'";
1117 $resql = $this->db->query($sql);
1118 if ($resql) {
1119 $obj = $this->db->fetch_object($resql);
1120 $selectedgroups[] = $obj->code;
1121 while ($obj->fk_parent > 0) {
1122 $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)."'";
1123 $resql = $this->db->query($sql);
1124 if ($resql) {
1125 $obj = $this->db->fetch_object($resql);
1126 $selectedgroups[] = $obj->code;
1127 }
1128 }
1129 }
1130 }
1131
1132 $arrayidused = array();
1133 $arrayidusedconcat = array();
1134 $arraycodenotparent = array();
1135 $arraycodenotparent[] = "";
1136
1137 $stringtoprint = '<span class="supportemailfield bold">'.$langs->trans("GroupOfTicket").'</span> ';
1138 $stringtoprint .= '<select id="'.$htmlname.'" class="minwidth500" child_id="0">';
1139 $stringtoprint .= '<option value="">&nbsp;</option>';
1140
1141 $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctc.public, ";
1142 $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";
1143 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc";
1144 $sql .= " WHERE ctc.active > 0 AND ctc.entity = ".((int) $conf->entity);
1145 $public = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1146 if ($public) {
1147 $sql .= " AND ctc.public = 1";
1148 }
1149 $sql .= " AND ctc.fk_parent = 0";
1150 $sql .= $this->db->order('ctc.pos', 'ASC');
1151
1152 $resql = $this->db->query($sql);
1153 if ($resql) {
1154 $num_rows_level0 = $this->db->num_rows($resql);
1155 $i = 0;
1156 while ($i < $num_rows_level0) {
1157 $obj = $this->db->fetch_object($resql);
1158 if ($obj) {
1159 $label = ($obj->label != '-' ? $obj->label : '');
1160 if ($outputlangs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code) {
1161 $label = $outputlangs->trans("TicketCategoryShort".$obj->code);
1162 } elseif ($outputlangs->trans($obj->code) != $obj->code) {
1163 $label = $outputlangs->trans($obj->code);
1164 }
1165
1166 $grouprowid = $obj->rowid;
1167 $groupvalue = $obj->code;
1168 $grouplabel = $label;
1169
1170 $isparent = $obj->isparent;
1171 if (is_array($selectedgroups)) {
1172 $iselected = in_array($obj->code, $selectedgroups) ? 'selected' : '';
1173 } else {
1174 $iselected = $groupticket == $obj->code ? 'selected' : '';
1175 }
1176 $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>';
1177 if ($isparent == 'NOTPARENT') {
1178 $arraycodenotparent[] = $groupvalue;
1179 }
1180 $arrayidused[] = $grouprowid;
1181 $arrayidusedconcat[] = $grouprowid;
1182 }
1183 $i++;
1184 }
1185 } else {
1186 dol_print_error($this->db);
1187 }
1188 if (count($arrayidused) == 1) {
1189 return '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.dol_escape_htmltag($groupvalue).'">';
1190 } else {
1191 $stringtoprint .= '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'_select" class="maxwidth500 minwidth400" value="'.GETPOST($htmlname).'">';
1192 $stringtoprint .= '<input type="hidden" name="'.$htmlname.'_child_id" id="'.$htmlname.'_select_child_id" class="maxwidth500 minwidth400" '.GETPOST($htmlname).' value="'.GETPOST($htmlname."_child_id").'">';
1193 }
1194 $stringtoprint .= '</select>&nbsp;';
1195
1196 $levelid = 1; // The first combobox
1197 while ($levelid <= $use_multilevel) { // Loop to take the child of the combo
1198 $tabscript = array();
1199 $stringtoprint .= '<select id="'.$htmlname.'_child_'.$levelid.'" class="maxwidth500 minwidth400 groupticketchild" child_id="'.$levelid.'">';
1200 $stringtoprint .= '<option value="">&nbsp;</option>';
1201
1202 $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctc.public, ctcjoin.code as codefather";
1203 $sql .= " FROM ".$this->db->prefix()."c_ticket_category as ctc";
1204 $sql .= " JOIN ".$this->db->prefix()."c_ticket_category as ctcjoin ON ctc.fk_parent = ctcjoin.rowid";
1205 $sql .= " WHERE ctc.active > 0 AND ctc.entity = ".((int) $conf->entity);
1206 $sql .= " AND ctc.rowid NOT IN (".$this->db->sanitize(implode(',', $arrayidusedconcat)).")";
1207
1208 $public = ($filtertype == 'public=1' || $filtertype == '(public:=:1)');
1209 if ($public) {
1210 $sql .= " AND ctc.public = 1";
1211 }
1212 // Add a test to take only record that are direct child
1213 if (!empty($arrayidused)) {
1214 $sql .= " AND ctc.fk_parent IN ( ";
1215 foreach ($arrayidused as $idused) {
1216 $sql .= $idused.", ";
1217 }
1218 $sql = substr($sql, 0, -2);
1219 $sql .= ")";
1220 }
1221 $sql .= $this->db->order('ctc.pos', 'ASC');
1222
1223 $resql = $this->db->query($sql);
1224 if ($resql) {
1225 $num_rows = $this->db->num_rows($resql);
1226 $i = 0;
1227 $arrayidused = array();
1228 while ($i < $num_rows) {
1229 $obj = $this->db->fetch_object($resql);
1230 if ($obj) {
1231 $label = ($obj->label != '-' ? $obj->label : '');
1232 if ($outputlangs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code) {
1233 $label = $outputlangs->trans("TicketCategoryShort".$obj->code);
1234 } elseif ($outputlangs->trans($obj->code) != $obj->code) {
1235 $label = $outputlangs->trans($obj->code);
1236 }
1237
1238 $grouprowid = $obj->rowid;
1239 $groupvalue = $obj->code;
1240 $grouplabel = $label;
1241 $isparent = $obj->isparent;
1242 $fatherid = $obj->fk_parent;
1243 $arrayidused[] = $grouprowid;
1244 $arrayidusedconcat[] = $grouprowid;
1245 $groupcodefather = $obj->codefather;
1246 if ($isparent == 'NOTPARENT') {
1247 $arraycodenotparent[] = $groupvalue;
1248 }
1249 if (is_array($selectedgroups)) {
1250 $iselected = in_array($obj->code, $selectedgroups) ? 'selected' : '';
1251 } else {
1252 $iselected = $groupticket == $obj->code ? 'selected' : '';
1253 }
1254 $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>';
1255 if (empty($tabscript[$groupcodefather])) {
1256 $tabscript[$groupcodefather] = 'if ($("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").val() == "'.dol_escape_js($groupcodefather).'"){
1257 $(".'.$htmlname.'_'.dol_escape_htmltag($fatherid).'_child_'.$levelid.'").show()
1258 console.log("We show child tickets of '.$groupcodefather.' group ticket")
1259 }else{
1260 $(".'.$htmlname.'_'.dol_escape_htmltag($fatherid).'_child_'.$levelid.'").hide()
1261 console.log("We hide child tickets of '.$groupcodefather.' group ticket")
1262 }';
1263 }
1264 }
1265 $i++;
1266 }
1267 } else {
1268 dol_print_error($this->db);
1269 }
1270 $stringtoprint .= '</select>';
1271
1272 $stringtoprint .= '<script nonce="'.getNonce().'">';
1273 $stringtoprint .= 'arraynotparents = '.json_encode($arraycodenotparent).';'; // when the last visible combo list is number x, this is the array of group
1274 $stringtoprint .= 'if (arraynotparents.includes($("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").val())){
1275 console.log("'.$htmlname.'_child_'.$levelid.'")
1276 if($("#'.$htmlname.'_child_'.$levelid.'").val() == "" && ($("#'.$htmlname.'_child_'.$levelid.'").attr("child_id")>'.$child_id.')){
1277 $("#'.$htmlname.'_child_'.$levelid.'").hide();
1278 console.log("We hide '.$htmlname.'_child_'.$levelid.' input")
1279 }
1280 if(arraynotparents.includes("'.$groupticket.'") && '.$child_id.' == 0){
1281 $("#ticketcategory_select_child_id").val($("#'.$htmlname.'").attr("child_id"))
1282 $("#ticketcategory_select").val($("#'.$htmlname.'").val()) ;
1283 console.log("We choose '.$htmlname.' input and reload hidden input");
1284 }
1285 }
1286 $("#'.$htmlname.($levelid > 1 ? '_child_'.($levelid - 1) : '').'").change(function() {
1287 child_id = $("#'.$htmlname.($levelid > 1 ? '_child_'.$levelid : '').'").attr("child_id");
1288
1289 /* Change of value to select this value*/
1290 if (arraynotparents.includes($(this).val()) || $(this).attr("child_id") == '.$use_multilevel.') {
1291 $("#ticketcategory_select").val($(this).val());
1292 $("#ticketcategory_select_child_id").val($(this).attr("child_id")) ;
1293 console.log("We choose to select "+ $(this).val());
1294 }else{
1295 if ($("#'.$htmlname.'_child_'.$levelid.' option").length <= 1) {
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() + " and next combo has no item, so we keep this selection");
1299 } else {
1300 console.log("We choose to select "+ $(this).val() + " but next combo has some item, so we clean selected item");
1301 $("#ticketcategory_select").val("");
1302 $("#ticketcategory_select_child_id").val("");
1303 }
1304 }
1305
1306 console.log("We select a new value into combo child_id="+child_id);
1307
1308 // Hide all selected box that are child of the one modified
1309 $(".groupticketchild").each(function(){
1310 if ($(this).attr("child_id") > child_id) {
1311 console.log("hide child_id="+$(this).attr("child_id"));
1312 $(this).val("");
1313 $(this).hide();
1314 }
1315 })
1316
1317 // Now we enable the next combo
1318 $("#'.$htmlname.'_child_'.$levelid.'").val("");
1319 if (!arraynotparents.includes($(this).val()) && $("#'.$htmlname.'_child_'.$levelid.' option").length > 1) {
1320 console.log($("#'.$htmlname.'_child_'.$levelid.' option").length);
1321 $("#'.$htmlname.'_child_'.$levelid.'").show()
1322 } else {
1323 $("#'.$htmlname.'_child_'.$levelid.'").hide()
1324 }
1325 ';
1326 $levelid++;
1327 foreach ($tabscript as $script) {
1328 $stringtoprint .= $script;
1329 }
1330 $stringtoprint .= '})';
1331 $stringtoprint .= '</script>';
1332 }
1333 $stringtoprint .= '<script nonce="'.getNonce().'">';
1334 $stringtoprint .= '$("#'.$htmlname.'_child_'.$use_multilevel.'").change(function() {
1335 $("#ticketcategory_select").val($(this).val());
1336 $("#ticketcategory_select_child_id").val($(this).attr("child_id"));
1337 tmpvalselect = $("#ticketcategory_select").val();
1338 if(tmpvalselect == "" && $("#ticketcategory_select_child_id").val() >= 1){
1339 $("#ticketcategory_select_child_id").val($(this).attr("child_id")-1);
1340 }
1341 console.log($("#ticketcategory_select").val());
1342 })';
1343 $stringtoprint .= '</script>';
1344 $stringtoprint .= ajax_combobox($htmlname);
1345
1346 return $stringtoprint;
1347 }
1348 }
1349
1363 public function selectSeveritiesTickets($selected = '', $htmlname = 'ticketseverity', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '')
1364 {
1365 global $conf, $langs, $user;
1366
1367 $ticketstat = new Ticket($this->db);
1368
1369 dol_syslog(get_class($this)."::selectSeveritiesTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
1370
1371 $filterarray = array();
1372
1373 if ($filtertype != '' && $filtertype != '-1') {
1374 $filterarray = explode(',', $filtertype);
1375 }
1376
1377 $ticketstat->loadCacheSeveritiesTickets();
1378
1379 print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1380 if ($empty) {
1381 print '<option value="">'.((is_numeric($empty) || $empty == 'ifone') ? '&nbsp;' : $empty).'</option>';
1382 }
1383
1384 if (is_array($conf->cache['severity_tickets']) && count($conf->cache['severity_tickets'])) {
1385 foreach ($conf->cache['severity_tickets'] as $id => $arrayseverities) {
1386 // On passe si on a demande de filtrer sur des modes de paiments particuliers
1387 if (count($filterarray) && !in_array($arrayseverities['type'], $filterarray)) {
1388 continue;
1389 }
1390
1391 // We discard empty line if showempty is on because an empty line has already been output.
1392 if ($empty && empty($arrayseverities['code'])) {
1393 continue;
1394 }
1395
1396 if ($format == 0) {
1397 print '<option value="'.$id.'"';
1398 }
1399
1400 if ($format == 1) {
1401 print '<option value="'.$arrayseverities['code'].'"';
1402 }
1403
1404 if ($format == 2) {
1405 print '<option value="'.$arrayseverities['code'].'"';
1406 }
1407
1408 if ($format == 3) {
1409 print '<option value="'.$id.'"';
1410 }
1411
1412 // If text is selected, we compare with code, otherwise with id
1413 if (isset($selected) && preg_match('/[a-z]/i', $selected) && $selected == $arrayseverities['code']) {
1414 print ' selected="selected"';
1415 } elseif (isset($selected) && $selected == $id) {
1416 print ' selected="selected"';
1417 } elseif ($arrayseverities['use_default'] == "1" && empty($selected) && (!$empty || $empty == 'ifone')) {
1418 print ' selected="selected"';
1419 } elseif (count($conf->cache['severity_tickets']) == 1 && (!$empty || $empty == 'ifone')) { // If only 1 choice, we autoselect it
1420 print ' selected="selected"';
1421 }
1422
1423 print '>';
1424
1425 $value = '';
1426 if ($format == 0) {
1427 $value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
1428 }
1429
1430 if ($format == 1) {
1431 $value = $arrayseverities['code'];
1432 }
1433
1434 if ($format == 2) {
1435 $value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
1436 }
1437
1438 if ($format == 3) {
1439 $value = $arrayseverities['code'];
1440 }
1441
1442 print $value ? $value : '&nbsp;';
1443 print '</option>';
1444 }
1445 }
1446 print '</select>';
1447 if (isset($user->admin) && $user->admin && !$noadmininfo) {
1448 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1449 }
1450
1451 print ajax_combobox('select'.$htmlname);
1452 }
1453
1454 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1460 public function clear_attached_files()
1461 {
1462 // phpcs:enable
1463 global $conf, $user;
1464 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1465
1466 // Set tmp user directory
1467 $vardir = $conf->user->dir_output."/".$user->id;
1468 $upload_dir = $vardir.'/temp/'; // TODO Add $keytoavoidconflict in upload_dir path
1469 if (is_dir($upload_dir)) {
1470 dol_delete_dir_recursive($upload_dir);
1471 }
1472
1473 if (!empty($this->trackid)) { // TODO Always use trackid (ticXXX) instead of track_id (abcd123)
1474 $keytoavoidconflict = '-'.$this->trackid;
1475 } else {
1476 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id;
1477 }
1478 unset($_SESSION["listofpaths".$keytoavoidconflict]);
1479 unset($_SESSION["listofnames".$keytoavoidconflict]);
1480 unset($_SESSION["listofmimes".$keytoavoidconflict]);
1481 }
1482
1490 public function showMessageForm($width = '40%', $fromPublicInterface = 0)
1491 {
1492 global $conf, $langs, $user, $hookmanager, $form;
1493
1494 $formmail = new FormMail($this->db);
1495 $addfileaction = 'addfile';
1496
1497 if (!is_object($form)) {
1498 $form = new Form($this->db);
1499 }
1500
1501 // Load translation files required by the page
1502 $langs->loadLangs(array('other', 'mails', 'ticket'));
1503
1504 // Clear temp files. Must be done at beginning, before call of triggers
1505 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
1506 $this->clear_attached_files();
1507 }
1508
1509 // Define output language
1510 $outputlangs = $langs;
1511 $newlang = '';
1512 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->param['langsmodels'])) {
1513 $newlang = $this->param['langsmodels'];
1514 }
1515 if (!empty($newlang)) {
1516 $outputlangs = new Translate("", $conf);
1517 $outputlangs->setDefaultLang($newlang);
1518 $outputlangs->load('other');
1519 }
1520
1521 // Get message template for $this->param["models"] into c_email_templates
1522 $arraydefaultmessage = -1;
1523 if (isset($this->param['models']) && $this->param['models'] != 'none') {
1524 $model_id = 0;
1525 if (array_key_exists('models_id', $this->param)) {
1526 $model_id = (int) $this->param["models_id"];
1527 }
1528
1529 // If $model_id is empty, preselect the first one
1530 $usedefault = ($model_id ? -1 : 1);
1531 $arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id, 1, '', $usedefault);
1532 if (isset($arraydefaultmessage->id) && empty($model_id)) {
1533 $model_id = $arraydefaultmessage->id;
1534 $this->param['models_id'] = $model_id;
1535 }
1536 }
1537
1538 // Define list of attached files
1539 $listofpaths = array();
1540 $listofnames = array();
1541 $listofmimes = array();
1542
1543 if (!empty($this->trackid)) {
1544 $keytoavoidconflict = '-'.$this->trackid;
1545 } else {
1546 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id; // track_id instead of trackid
1547 }
1548 //var_dump($keytoavoidconflict);
1549 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
1550 if (!empty($arraydefaultmessage->joinfiles) && !empty($this->param['fileinit']) && is_array($this->param['fileinit'])) {
1551 foreach ($this->param['fileinit'] as $path) {
1552 $formmail->add_attached_files($path, basename($path), dol_mimetype($path));
1553 }
1554 }
1555 }
1556 //var_dump($_SESSION);
1557 //var_dump($_SESSION["listofpaths".$keytoavoidconflict]);
1558 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
1559 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
1560 }
1561 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
1562 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
1563 }
1564 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
1565 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
1566 }
1567
1568 // Define output language
1569 $outputlangs = $langs;
1570 $newlang = '';
1571 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->param['langsmodels'])) {
1572 $newlang = $this->param['langsmodels'];
1573 }
1574 if (!empty($newlang)) {
1575 $outputlangs = new Translate("", $conf);
1576 $outputlangs->setDefaultLang($newlang);
1577 $outputlangs->load('other');
1578 }
1579
1580 print "\n<!-- Begin message_form TICKET -->\n";
1581
1582 $send_email = GETPOSTINT('send_email') ? GETPOSTINT('send_email') : 0;
1583
1584 // Example 1 : Adding jquery code
1585 print '<script nonce="'.getNonce().'" type="text/javascript">
1586 jQuery(document).ready(function() {
1587 send_email='.((int) $send_email).';
1588 if (send_email) {
1589 if (!jQuery("#send_msg_email").is(":checked")) {
1590 jQuery("#send_msg_email").prop("checked", true).trigger("change");
1591 }
1592 jQuery(".email_line").show();
1593 jQuery(".not_email_line").hide();
1594 } else {
1595 if (!jQuery("#private_message").is(":checked")) {
1596 jQuery("#private_message").prop("checked", true).trigger("change");
1597 }
1598 jQuery(".email_line").hide();
1599 jQuery(".not_email_line").show();
1600 }
1601 ';
1602
1603 // If constant set, allow to send private messages as email
1604 if (!getDolGlobalString('TICKET_SEND_PRIVATE_EMAIL')) {
1605 print 'jQuery("#send_msg_email").click(function() {
1606 console.log("Click send_msg_email");
1607 if(jQuery(this).is(":checked")) {
1608 if (jQuery("#private_message").is(":checked")) {
1609 jQuery("#private_message").prop("checked", false).trigger("change");
1610 }
1611 jQuery(".email_line").show();
1612 jQuery(".not_email_line").hide();
1613 }
1614 else {
1615 jQuery(".email_line").hide();
1616 jQuery(".not_email_line").show();
1617 }
1618 });
1619
1620 jQuery("#private_message").click(function() {
1621 console.log("Click private_message");
1622 if (jQuery(this).is(":checked")) {
1623 if (jQuery("#send_msg_email").is(":checked")) {
1624 jQuery("#send_msg_email").prop("checked", false).trigger("change");
1625 }
1626 jQuery(".email_line").hide();
1627 jQuery(".not_email_line").show();
1628 }
1629 });';
1630 }
1631
1632 print '});
1633 </script>';
1634
1635 print '<form method="post" name="ticket" id="ticket" enctype="multipart/form-data" action="'.$this->param["returnurl"].'">';
1636 print '<input type="hidden" name="token" value="'.newToken().'">';
1637 print '<input type="hidden" name="action" value="'.$this->action.'">';
1638 print '<input type="hidden" name="actionbis" value="add_message">';
1639 print '<input type="hidden" name="backtopage" value="'.$this->backtopage.'">';
1640 if (!empty($this->trackid)) {
1641 print '<input type="hidden" name="trackid" value="'.$this->trackid.'">';
1642 } else {
1643 print '<input type="hidden" name="trackid" value="'.(empty($this->track_id) ? '' : $this->track_id).'">';
1644 $keytoavoidconflict = empty($this->track_id) ? '' : '-'.$this->track_id; // track_id instead of trackid
1645 }
1646 foreach ($this->param as $key => $value) {
1647 print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
1648 }
1649
1650 // Get message template
1651 $model_id = 0;
1652 if (array_key_exists('models_id', $this->param)) {
1653 $model_id = $this->param["models_id"];
1654 $usedefault = ($model_id ? -1 : 1);
1655 $arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id, 1, '', $usedefault);
1656 }
1657
1658 $result = $formmail->fetchAllEMailTemplate(!empty($this->param["models"]) ? $this->param["models"] : "", $user, $outputlangs);
1659 if ($result < 0) {
1660 setEventMessages($this->error, $this->errors, 'errors');
1661 }
1662 $modelmail_array = array();
1663 foreach ($formmail->lines_model as $line) {
1664 $modelmail_array[$line->id] = $line->label;
1665 }
1666
1667 $ticketstat = new Ticket($this->db);
1668 $res = $ticketstat->fetch(0, '', $this->track_id);
1669
1670 print '<table class="border" width="'.$width.'">';
1671
1672 // External users can't send message email
1673 if ($user->hasRight("ticket", "write") && !$user->socid) {
1674 print '<tr><td class="width200"></td><td>';
1675 $checkbox_selected = (GETPOST('send_email') == "1" ? ' checked' : (getDolGlobalInt('TICKETS_MESSAGE_FORCE_MAIL') ? 'checked' : ''));
1676 print '<input type="checkbox" name="send_email" value="1" id="send_msg_email" '.$checkbox_selected.'/> ';
1677 print '<label for="send_msg_email">'.$langs->trans('SendMessageByEmail').'</label>';
1678 $texttooltip = $langs->trans("TicketMessageSendEmailHelp");
1679 if (!getDolGlobalString('TICKET_SEND_PRIVATE_EMAIL')) {
1680 $texttooltip .= ' '.$langs->trans("TicketMessageSendEmailHelp2b");
1681 } else {
1682 $texttooltip .= ' '.$langs->trans("TicketMessageSendEmailHelp2a", '{s1}');
1683 }
1684 $texttooltip = str_replace('{s1}', $langs->trans('MarkMessageAsPrivate'), $texttooltip);
1685 print ' '.$form->textwithpicto('', $texttooltip, 1, 'help');
1686
1687 // Section to selection email template
1688 if (count($modelmail_array) > 0) {
1689 print ' &nbsp; <span class="email_line">';
1690 print $formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], $langs->trans('SelectMailModel'), 0, 0, "", 0, 0, 0, '', 'minwidth200');
1691 if ($user->admin) {
1692 print info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv("Tools").' - '.$langs->transnoentitiesnoconv("EMailTemplates")), 1);
1693 }
1694 print ' &nbsp; ';
1695 print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
1696 print '</span>';
1697 }
1698
1699 print '</td></tr>';
1700
1701 // Private message (not visible by customer/external user)
1702 if (!$user->socid) {
1703 print '<tr><td></td><td>';
1704 $checkbox_selected = (GETPOST('private_message', 'alpha') == "1" ? ' checked' : '');
1705 print '<input type="checkbox" name="private_message" value="1" id="private_message" '.$checkbox_selected.'/> ';
1706 print '<label for="private_message">'.$langs->trans('MarkMessageAsPrivate').'</label>';
1707 print ' '.$form->textwithpicto('', $langs->trans("TicketMessagePrivateHelp"), 1, 'help');
1708 print '</td></tr>';
1709 }
1710
1711 // Zone to select its email template
1712 /*
1713 if (count($modelmail_array) > 0) {
1714 print '<tr class="email_line"><td></td><td colspan="2"><div style="padding: 3px 0 3px 0">'."\n";
1715 print $formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], $langs->trans('SelectMailModel'), 0, 0, "", 0, 0, 0, '', 'minwidth200');
1716 if ($user->admin) {
1717 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1718 }
1719 print ' &nbsp; ';
1720 print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
1721 print '</div></td>';
1722 }
1723 */
1724
1725 // From (and Reply-To if defined)
1726 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
1727 $replyto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_REPLYTO');
1728 print '<tr class="email_line"><td class="width200"><span class="">'.$langs->trans("MailFrom");
1729 if ($replyto) {
1730 print ' <span class="opacitymedium">('.$langs->trans("MailReply").')</span>';
1731 }
1732 print '</span></td>';
1733 print '<td><span class="">'.img_picto('', 'email', 'class="pictofixedwidth"').$from;
1734 if ($replyto) {
1735 print ' <span class="opacitymedium">('.$replyto.')</span>';
1736 }
1737 print '</span></td></tr>';
1738
1739 // Recipients / adressed-to
1740 $parameters = array();
1741 $action = '';
1742 $reshook = $hookmanager->executeHooks('printFieldTicketEmailTo', $parameters, $this, $action);
1743 if (empty($reshook)) {
1744 print '<tr class="email_line"><td>'.$langs->trans('MailRecipients');
1745 print ' '.$form->textwithpicto('', $langs->trans("TicketMessageRecipientsHelp"), 1, 'help');
1746 print '</td><td>';
1747 if ($res) {
1748 // Retrieve email of all contacts (internal and external)
1749 $contacts = $ticketstat->getInfosTicketInternalContact(1);
1750 $contacts = array_merge($contacts, $ticketstat->getInfosTicketExternalContact(1));
1751
1752 $sendto = array();
1753
1754 // Build array to display recipient list
1755 if (is_array($contacts) && count($contacts) > 0) {
1756 foreach ($contacts as $key => $info_sendto) {
1757 if ($info_sendto['email'] != '') {
1758 $sendto[] = dol_escape_htmltag(trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">").' <small class="opacitymedium">('.dol_escape_htmltag($info_sendto['libelle']).")</small>";
1759 }
1760 }
1761 }
1762
1763 if (!empty($ticketstat->origin_replyto) && !in_array($ticketstat->origin_replyto, $sendto)) {
1764 $sendto[] = dol_escape_htmltag((string) $ticketstat->origin_replyto).' <small class="opacitymedium">('.$langs->trans("TicketEmailOriginIssuer").")</small>";
1765 } elseif ($ticketstat->origin_email && !in_array($ticketstat->origin_email, $sendto)) {
1766 $sendto[] = dol_escape_htmltag((string) $ticketstat->origin_email).' <small class="opacitymedium">('.$langs->trans("TicketEmailOriginIssuer").")</small>";
1767 }
1768
1769 if ($ticketstat->fk_soc > 0) {
1770 $ticketstat->socid = $ticketstat->fk_soc;
1771 $ticketstat->fetch_thirdparty();
1772
1773 if (!empty($ticketstat->thirdparty->email) && !in_array($ticketstat->thirdparty->email, $sendto)) {
1774 $sendto[] = $ticketstat->thirdparty->email.' <small class="opacitymedium">('.$langs->trans('Customer').')</small>';
1775 }
1776 }
1777
1778 if (getDolGlobalInt('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS')) {
1779 $sendto[] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO').' <small class="opacitymedium">(generic email)</small>';
1780 }
1781
1782 // Print recipient list
1783 if (is_array($sendto) && count($sendto) > 0) {
1784 print img_picto('', 'email', 'class="pictofixedwidth"');
1785 print implode(', ', $sendto);
1786 } else {
1787 print '<div class="warning">'.$langs->trans('WarningNoEMailsAdded').' '.$langs->trans('TicketGoIntoContactTab').'</div>';
1788 }
1789 }
1790 print '</td></tr>';
1791 } else {
1792 print $hookmanager->resPrint;
1793 }
1794 // Send to CC
1795 $sendtocc = getDolGlobalString('TICKET_SEND_INTERNAL_CC');
1796 if ($sendtocc) {
1797 print '<tr class="email_line"><td><span class="">'.$langs->trans("MailCC").'</span></td>';
1798 print '<td><span class="">'.img_picto('', 'email', 'class="pictofixedwidth"').$sendtocc.'</span></td></tr>';
1799 }
1800 }
1801
1802 $uselocalbrowser = false;
1803
1804 // Summary
1805 if ($this->withtitletopic) {
1806 print '<tr class="not_email_line"><td><label for="summary"><span>'.$langs->trans("Summary").'</span></label></td><td>';
1807 // Answer to a ticket : display of the thread title in readonly
1808 if ($this->withtopicreadonly && $this->topic_title) {
1809 print $langs->trans('Summary').' '.$this->topic_title;
1810 } elseif (empty($this->withtopicreadonly)) {
1811 $subject = $this->topic_title;
1812 print '<input class="text minwidth500" maxlength="42" id="summary" name="summary" placeholder="" value="'.dolPrintHTMLForAttribute($subject).'"'.(empty($this->withemail) ? ' autofocus' : '').' />';
1813 }
1814 print '</td></tr>';
1815 }
1816
1817 $topic = "";
1818 foreach ($formmail->lines_model as $line) {
1819 if (!empty($this->substit) && $this->param['models_id'] == $line->id) {
1820 $topic = make_substitutions($line->topic, $this->substit);
1821 break;
1822 }
1823 }
1824 print '<tr class="email_line"><td class="fieldrequired">'.$langs->trans('MailTopic').'</td>';
1825 if (empty($topic)) {
1826 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">';
1827 } else {
1828 print '<td><input type="text" class="text minwidth500" name="subject" value="'.make_substitutions($topic, $this->substit).'" spellcheck="false">';
1829 }
1830 print '</td></tr>';
1831
1832 // Attached files
1833 if (!empty($this->withfile)) {
1834 $out = '<tr>';
1835 $out .= '<td>'.$langs->trans("MailFile").'</td>';
1836 $out .= '<td>';
1837 // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
1838 $out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
1839 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
1840 $out .= 'jQuery(document).ready(function () {';
1841 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", true);';
1842 $out .= ' jQuery("#addedfile").on("change", function() {';
1843 $out .= ' if (jQuery(this).val().length) {';
1844 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", false);';
1845 $out .= ' } else {';
1846 $out .= ' jQuery("#'.$addfileaction.'").prop("disabled", true);';
1847 $out .= ' }';
1848 $out .= ' });';
1849 $out .= ' jQuery(".removedfile").click(function() {';
1850 $out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
1851 $out .= ' });';
1852 $out .= '})';
1853 $out .= '</script>'."\n";
1854
1855 if (count($listofpaths)) {
1856 foreach ($listofpaths as $key => $val) {
1857 $out .= '<div id="attachfile_'.$key.'">';
1858 $out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
1859 if (!$this->withfilereadonly) {
1860 $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.'" />';
1861 }
1862 $out .= '<br></div>';
1863 }
1864 } else {
1865 //$out .= $langs->trans("NoAttachedFiles").'<br>';
1866 }
1867 if ($this->withfile == 2) { // Can add other files
1868 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
1869 $out .= ' ';
1870 $out .= '<input type="submit" class="button smallpaddingimp reposition" id="'.$addfileaction.'" name="'.$addfileaction.'" value="'.$langs->trans("MailingAddFile").'" />';
1871 }
1872 $out .= "</td></tr>\n";
1873
1874 print $out;
1875 }
1876
1877 // MESSAGE
1878 $defaultmessage = "";
1879 if (is_object($arraydefaultmessage) && $arraydefaultmessage->content) {
1880 $defaultmessage = (string) $arraydefaultmessage->content;
1881 }
1882 $defaultmessage = str_replace('\n', "\n", $defaultmessage);
1883
1884 // Deal with format differences between message and signature (text / HTML)
1885 if (dol_textishtml($defaultmessage) && !dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1886 $this->substit['__USER_SIGNATURE__'] = dol_nl2br($this->substit['__USER_SIGNATURE__']);
1887 } elseif (!dol_textishtml($defaultmessage) && isset($this->substit['__USER_SIGNATURE__']) && dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1888 $defaultmessage = dol_nl2br($defaultmessage);
1889 }
1890 if (GETPOSTISSET("message") && !GETPOST('modelselected')) {
1891 $defaultmessage = GETPOST('message', 'restricthtml');
1892 } else {
1893 $defaultmessage = make_substitutions($defaultmessage, $this->substit);
1894 // Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty)
1895 $defaultmessage = preg_replace("/^(<br>)+/", "", $defaultmessage);
1896 $defaultmessage = preg_replace("/^\n+/", "", $defaultmessage);
1897 }
1898
1899 $ckeditorenabledforticket = (getDolGlobalString('FCKEDITOR_ENABLE_TICKET') >= ($fromPublicInterface ? 2 : 1)); // 0=no, 1=from backoffice only, 2=from backoffice+public (very dangerous)
1900
1901 print '<!-- Message line from showMessageForm -->';
1902 print '<tr><td class="tdtop"><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span>';
1903 if ($user->hasRight("ticket", "write") && !$user->socid) {
1904 $texttooltip = $langs->trans("TicketMessageHelp");
1905 if (getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO') || getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE')) {
1906 $texttooltip .= '<br><br>'.$langs->trans("ForEmailMessageWillBeCompletedWith").'...';
1907 }
1908 $allowedmailtags = array('a', 'div', 'strong', 'em', 'i', 'u', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img');
1909 if (getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO')) {
1910 $mail_intro = make_substitutions(getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO'), $this->substit);
1911 print '<input type="hidden" name="mail_intro" value="'.dolPrintHTMLForAttribute($mail_intro, 0, $allowedmailtags).'">';
1912 $texttooltip .= '<br><u>'.$langs->trans("TicketMessageMailIntro").'</u><br>'.$mail_intro;
1913 }
1914 if (getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE')) {
1915 $mail_signature = make_substitutions(getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE'), $this->substit);
1916 print '<input type="hidden" name="mail_signature" value="'.dolPrintHTMLForAttribute($mail_signature, 0, $allowedmailtags).'">';
1917 $texttooltip .= '<br><br><u>'.$langs->trans("TicketMessageMailFooter").'</u><br>'.$mail_signature;
1918 }
1919 print $form->textwithpicto('', $texttooltip, 1, 'help');
1920 }
1921 print '</label></td>';
1922 $out = '<td class="tdtop">';
1923
1924 $out .= '<div class="div-layoutai">';
1925
1926 // Required to show editor assistants
1927 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
1928 $formmail = new FormMail($this->db);
1929
1930 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formai.class.php';
1931 $formai = new FormAI($this->db);
1932
1933 $formmail->withfckeditor = $ckeditorenabledforticket ? 1 : 0;
1934 $formmail->withlayout = ((string) $ckeditorenabledforticket && !$fromPublicInterface) ? 'email' : '';
1935 $formmail->withaiprompt = (isModEnabled('ai') && !$fromPublicInterface) ? 'text' : '';
1936
1937 $showlinktolayout = ($formmail->withfckeditor && getDolGlobalInt('MAIN_EMAIL_USE_LAYOUT')) ? $formmail->withlayout : '';
1938 $showlinktolayoutlabel = $langs->trans("FillMessageWithALayout");
1939 $showlinktoai = ($formmail->withaiprompt ? 'textgenerationemail' : '');
1940 $showlinktoailabel = $langs->trans("AIEnhancements");
1941 $formatforouput = '';
1942 $htmlname = 'message';
1943
1944 $formai->substit = $this->substit;
1945
1946 // Fill $out
1947 $db = $this->db;
1948 include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
1949
1950 $out .= '</td>';
1951 print $out;
1952
1953 print '</tr>';
1954
1955
1956 print '<tr><td colspan="2">';
1957 //$toolbarname = 'dolibarr_details';
1958 $toolbarname = 'dolibarr_notes';
1959 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1960 $uselocalbrowser = false;
1961 if (!$ckeditorenabledforticket) {
1962 $defaultmessage = dol_string_nohtmltag($defaultmessage, 2);
1963 }
1964
1965 $doleditor = new DolEditor('message', $defaultmessage, '100%', 200, $toolbarname, '', false, $uselocalbrowser, $ckeditorenabledforticket, ROWS_6, '90%');
1966 $doleditor->Create();
1967
1968 print '</td></tr>';
1969
1970 // Footer
1971 // External users can't send message email
1972 /*if ($user->rights->ticket->write && !$user->socid && !empty($conf->global->TICKET_MESSAGE_MAIL_SIGNATURE)) {
1973 $mail_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;
1974 print '<tr class="email_line"><td><label for="mail_intro">'.$langs->trans("TicketMessageMailFooter").'</label>';
1975 print $form->textwithpicto('', $langs->trans("TicketMessageMailFooterHelp"), 1, 'help');
1976 print '</td><td>';
1977 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1978 $doleditor = new DolEditor('mail_signature', $mail_signature, '100%', 90, 'dolibarr_details', '', false, $uselocalbrowser, getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_2, 70);
1979 $doleditor->Create();
1980 print '</td></tr>';
1981 }
1982 */
1983
1984 print '</table>';
1985
1986 print '<br><center>';
1987 print '<input type="submit" class="button" name="btn_add_message" value="'.$langs->trans("Add").'"';
1988 // Add a javascript test to avoid to forget to submit file before sending email
1989 if ($this->withfile == 2 && !empty($conf->use_javascript_ajax)) {
1990 print ' onClick="if (document.ticket.addedfile.value != \'\') { alert(\''.dol_escape_js($langs->trans("FileWasNotUploaded")).'\'); return false; } else { return true; }"';
1991 }
1992 print ' />';
1993 if (!empty($this->withcancel)) {
1994 print " &nbsp; &nbsp; ";
1995 print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
1996 }
1997 print "</center>\n";
1998
1999 print '<input type="hidden" name="page_y">'."\n";
2000
2001 print "</form><br>\n";
2002
2003 // Disable enter key if option MAIN_MAILFORM_DISABLE_ENTERKEY is set
2004 if (getDolGlobalString('MAIN_MAILFORM_DISABLE_ENTERKEY')) {
2005 print '<script type="text/javascript">';
2006 print 'jQuery(document).ready(function () {';
2007 print ' $(document).on("keypress", \'#ticket\', function (e) { /* Note this is called at every key pressed ! */
2008 var code = e.keyCode || e.which;
2009 if (code == 13) {
2010 console.log("Enter was intercepted and blocked");
2011 e.preventDefault();
2012 return false;
2013 }
2014 });';
2015 print '})';
2016 print '</script>';
2017 }
2018
2019 print "<!-- End message_form TICKET -->\n";
2020 }
2021}
$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)
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='')
Show information in HTML for admin users or standard users.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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.
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.
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
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.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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.