I’m new to Dolibarr and I’m looking to follow good practices in order to modify my site.
My first goal is to modify the email sent to the administrator when a ticket is created.
For this I created an “emailstemplates” module containing an “ActionsEmailsTemplates” class which overrides the “htdocs/public/ticket/create_ticket.php” file.
<?php
/* Copyright (C) 2022 Gaetan Vial <g.vial@medas.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file emailstemplates/class/actions_emailstemplates.class.php
* \ingroup emailstemplates
* \brief Example hook overload.
*
* Put detailed description here.
*/
/**
* Class ActionsEmailsTemplates
*/
class ActionsEmailsTemplates
{
/**
* @var DoliDB Database handler.
*/
public $db;
/**
* @var string Error code (or message)
*/
public $error = '';
/**
* @var array Errors
*/
public $errors = array();
/**
* @var array Hook results. Propagated to $hookmanager->resArray for later reuse
*/
public $results = array();
/**
* @var string String displayed by executeHook() immediately after return
*/
public $resprints;
/**
* @var int Priority of hook (50 is used if value is not defined)
*/
public $priority;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
$this->db = $db;
}
/**
* Overloading the doActions function : replacing the parent's function with the one below
*
* @param array $parameters Hook metadatas (context, etc...)
* @param CommonObject $object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
* @param string $action Current action (if set). Generally create or edit or null
* @param HookManager $hookmanager Hook manager propagated to allow calling another hook
* @return int < 0 on error, 0 on success, 1 to replace standard code
*/
public function doActions($parameters, &$object, &$action, $hookmanager)
{
global $conf, $user, $langs;
$error = 0; // Error counter
/* print_r($parameters); print_r($object); echo "action: " . $action; */
if (in_array($parameters['currentcontext'], array('publicnewticketcard'))) { // do something only for the context 'somecontext1' or 'somecontext2'
if (!defined('NOTOKENRENEWAL')) {
define('NOTOKENRENEWAL', '1');
}
if (!defined('NOREQUIREMENU')) {
define('NOREQUIREMENU', '1');
}
if (!defined('NOREQUIREHTML')) {
define('NOREQUIREHTML', '1');
}
if (!defined('NOLOGIN')) {
define("NOLOGIN", 1); // This means this output page does not require to be logged.
}
if (!defined('NOIPCHECK')) {
define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
}
if (!defined('NOBROWSERNOTIF')) {
define('NOBROWSERNOTIF', '1');
}
// For MultiCompany module.
// Do not use GETPOST here, function is not defined and define must be done before including main.inc.php
$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
if (is_numeric($entity)) {
define("DOLENTITY", $entity);
}
//var_dump('../../../main.inc.php'); die;
require_once DOL_DOCUMENT_ROOT.'/main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
// Load translation files required by the page
$langs->loadLangs(array('companies', 'other', 'mails', 'ticket'));
// Get parameters
$id = GETPOST('id', 'int');
$msg_id = GETPOST('msg_id', 'int');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$backtopage = '';
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager->initHooks(array('publicnewticketcard', 'globalcard'));
$object = new Ticket($db);
$extrafields = new ExtraFields($db);
$contacts = array();
$with_contact = null;
if (!empty($conf->global->TICKET_CREATE_THIRD_PARTY_WITH_CONTACT_IF_NOT_EXIST)) {
$with_contact = new Contact($db);
}
//$extrafields->fetch_name_optionals_label($object->table_element);
if (empty($conf->ticket->enabled)) {
accessforbidden('', 0, 0, 1);
}
if ($cancel) {
$backtopage = DOL_URL_ROOT.'/public/ticket/index.php';
header("Location: ".$backtopage);
exit;
}
if (GETPOST('addfile', 'alpha') && !GETPOST('save', 'alpha')) {
////$res = $object->fetch('','',GETPOST('track_id'));
////if($res > 0)
////{
include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
// Set tmp directory TODO Use a dedicated directory for temp mails files
$vardir = $conf->ticket->dir_output;
$upload_dir_tmp = $vardir.'/temp/'.session_id();
if (!dol_is_dir($upload_dir_tmp)) {
dol_mkdir($upload_dir_tmp);
}
dol_add_file_process($upload_dir_tmp, 0, 0, 'addedfile', '', null, '', 0);
$action = 'create_ticket';
////}
}
// Remove file
if (GETPOST('removedfile', 'alpha') && !GETPOST('save', 'alpha')) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
// Set tmp directory
$vardir = $conf->ticket->dir_output.'/';
$upload_dir_tmp = $vardir.'/temp/'.session_id();
// TODO Delete only files that was uploaded from email form
dol_remove_file_process(GETPOST('removedfile'), 0, 0);
$action = 'create_ticket';
}
if ($action == 'create_ticket' && GETPOST('save', 'alpha')) {
$error = 0;
$origin_email = GETPOST('email', 'alpha');
if (empty($origin_email)) {
$error++;
array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Email")));
$action = '';
} else {
// Search company saved with email
$searched_companies = $object->searchSocidByEmail($origin_email, '0');
// Chercher un contact existant avec cette adresse email
// Le premier contact trouvé est utilisé pour déterminer le contact suivi
$contacts = $object->searchContactByEmail($origin_email);
// Option to require email exists to create ticket
if (!empty($conf->global->TICKET_EMAIL_MUST_EXISTS) && !$contacts[0]->socid) {
$error++;
array_push($object->errors, $langs->trans("ErrorEmailMustExistToCreateTicket"));
$action = '';
}
}
$contact_lastname = '';
$contact_firstname = '';
$company_name = '';
$contact_phone = '';
if ($with_contact) {
// set linked contact to add in form
if (is_array($contacts) && count($contacts) == 1) {
$with_contact = current($contacts);
}
// check mandatory fields on contact
$contact_lastname = trim(GETPOST('contact_lastname', 'alphanohtml'));
$contact_firstname = trim(GETPOST('contact_firstname', 'alphanohtml'));
$company_name = trim(GETPOST('company_name', 'alphanohtml'));
$contact_phone = trim(GETPOST('contact_phone', 'alphanohtml'));
if (!($with_contact->id > 0)) {
// check lastname
if (empty($contact_lastname)) {
$error++;
array_push($object->errors, $langs->trans('ErrorFieldRequired', $langs->transnoentities('Lastname')));
$action = '';
}
// check firstname
if (empty($contact_firstname)) {
$error++;
array_push($object->errors, $langs->trans('ErrorFieldRequired', $langs->transnoentities('Firstname')));
$action = '';
}
}
}
if (!GETPOST("subject", "restricthtml")) {
$error++;
array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")));
$action = '';
}
if (!GETPOST("message", "restricthtml")) {
$error++;
array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Message")));
$action = '';
}
// Check email address
if (!empty($origin_email) && !isValidEmail($origin_email)) {
$error++;
array_push($object->errors, $langs->trans("ErrorBadEmailAddress", $langs->transnoentities("email")));
$action = '';
}
// Check Captcha code if is enabled
if (!empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA_TICKET)) {
$sessionkey = 'dol_antispam_value';
$ok = (array_key_exists($sessionkey, $_SESSION) === true && (strtolower($_SESSION[$sessionkey]) === strtolower(GETPOST('code', 'restricthtml'))));
if (!$ok) {
$error++;
array_push($object->errors, $langs->trans("ErrorBadValueForCode"));
$action = '';
}
}
if (!$error) {
$object->db->begin();
$object->track_id = generate_random_id(16);
$object->subject = GETPOST("subject", "restricthtml");
$object->message = GETPOST("message", "restricthtml");
$object->origin_email = $origin_email;
$object->type_code = GETPOST("type_code", 'aZ09');
$object->category_code = GETPOST("category_code", 'aZ09');
$object->severity_code = GETPOST("severity_code", 'aZ09');
if (!is_object($user)) {
$user = new User($db);
}
// create third-party with contact
$usertoassign = 0;
if ($with_contact && !($with_contact->id > 0)) {
$company = new Societe($db);
if (!empty($company_name)) {
$company->name = $company_name;
} else {
$company->particulier = 1;
$company->name = dolGetFirstLastname($contact_firstname, $contact_lastname);
}
$result = $company->create($user);
if ($result < 0) {
$error++;
$errors = ($company->error ? array($company->error) : $company->errors);
array_push($object->errors, $errors);
$action = 'create_ticket';
}
// create contact and link to this new company
if (!$error) {
$with_contact->email = $origin_email;
$with_contact->lastname = $contact_lastname;
$with_contact->firstname = $contact_firstname;
$with_contact->socid = $company->id;
$with_contact->phone_pro = $contact_phone;
$result = $with_contact->create($user);
if ($result < 0) {
$error++;
$errors = ($with_contact->error ? array($with_contact->error) : $with_contact->errors);
array_push($object->errors, $errors);
$action = 'create_ticket';
} else {
$contacts = array($with_contact);
}
}
}
if (is_array($searched_companies)) {
$object->fk_soc = $searched_companies[0]->id;
}
if (is_array($contacts) and count($contacts) > 0) {
$object->fk_soc = $contacts[0]->socid;
$usertoassign = $contacts[0]->id;
}
//$ret = $extrafields->setOptionalsFromPost(null, $object);
// Generate new ref
$object->ref = $object->getDefaultRef();
$object->context['disableticketemail'] = 1; // Disable emails sent by ticket trigger when creation is done from this page, emails are already sent later
$id = $object->create($user);
if ($id <= 0) {
$error++;
$errors = ($object->error ? array($object->error) : $object->errors);
array_push($object->errors, $object->error ? array($object->error) : $object->errors);
$action = 'create_ticket';
}
if (!$error && $id > 0) {
if ($usertoassign > 0) {
$object->add_contact($usertoassign, "SUPPORTCLI", 'external', 0);
}
}
if (!$error) {
$object->db->commit();
$action = "infos_success";
} else {
$object->db->rollback();
setEventMessages($object->error, $object->errors, 'errors');
$action = 'create_ticket';
}
if (!$error) {
$res = $object->fetch($id);
if ($res) {
// Create form object
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$formmail = new FormMail($db);
// Init to avoid errors
$filepath = array();
$filename = array();
$mimetype = array();
$attachedfiles = $formmail->get_attached_files();
$filepath = $attachedfiles['paths'];
$filename = $attachedfiles['names'];
$mimetype = $attachedfiles['mimes'];
// Send email to customer
$subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities('TicketNewEmailSubject', $object->ref, $object->track_id);
$message = ($conf->global->TICKET_MESSAGE_MAIL_NEW ? $conf->global->TICKET_MESSAGE_MAIL_NEW : $langs->transnoentities('TicketNewEmailBody')).'<br><br>';
$message .= $langs->transnoentities('TicketNewEmailBodyInfosTicket').'<br>';
$url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/view.php' : dol_buildpath('/public/ticket/view.php', 2)).'?track_id='.$object->track_id;
$infos_new_ticket = $langs->transnoentities('TicketNewEmailBodyInfosTrackId', '<a href="'.$url_public_ticket.'" rel="nofollow noopener">'.$object->track_id.'</a>').'<br>';
$infos_new_ticket .= $langs->transnoentities('TicketNewEmailBodyInfosTrackUrl').'<br><br>';
$message .= $infos_new_ticket;
$message .= getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE', $langs->transnoentities('TicketMessageMailSignatureText', $mysoc->name));
$sendto = GETPOST('email', 'alpha');
$from = $conf->global->MAIN_INFO_SOCIETE_NOM.' <'.getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM').'>';
$replyto = $from;
$sendtocc = '';
$deliveryreceipt = 0;
if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
$old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
$conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
}
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', 'tic'.$object->id, '', 'ticket');
if ($mailfile->error || $mailfile->errors) {
setEventMessages($mailfile->error, $mailfile->errors, 'errors');
} else {
$result = $mailfile->sendfile();
}
if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
$conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
}
// Send email to TICKET_NOTIFICATION_EMAIL_TO
$sendto = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
if ($sendto) {
$subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities('TicketNewEmailSubjectAdmin', $object->ref, $object->track_id);
$message_admin = $langs->transnoentities('TicketNewEmailBodyAdmin', $object->track_id).'<br><br>';
$message_admin .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
$message_admin .= '<li>le hook du module est bien utilisé pour modifier ce mail</li>';
$message_admin .= '<li>'.$langs->trans('Type').' : '.$object->type_label.'</li>';
$message_admin .= '<li>'.$langs->trans('Category').' : '.$object->category_label.'</li>';
$message_admin .= '<li>'.$langs->trans('Severity').' : '.$object->severity_label.'</li>';
$message_admin .= '<li>'.$langs->trans('From').' : '.$object->origin_email.'</li>';
// Extrafields
//$extrafields->fetch_name_optionals_label($object->table_element);
/*if (is_array($object->array_options) && count($object->array_options) > 0) {
foreach ($object->array_options as $key => $value) {
$key = substr($key, 8); // remove "options_"
$message_admin .= '<li>'.$langs->trans($extrafields->attributes[$object->table_element]['label'][$key]).' : '.$extrafields->showOutputField($key, $value, '', $object->table_element).'</li>';
}
}*/
$message_admin .= '</ul>';
$message_admin .= '<p>'.$langs->trans('Message').' : <br>'.$object->message.'</p>';
$message_admin .= '<p><a href="'.dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id.'" rel="nofollow noopener">'.$langs->trans('SeeThisTicketIntomanagementInterface').'</a></p>';
$from = $conf->global->MAIN_INFO_SOCIETE_NOM.' <'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>';
$replyto = $from;
if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
$old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
$conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
}
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', 'tic'.$object->id, '', 'ticket');
if ($mailfile->error || $mailfile->errors) {
setEventMessages($mailfile->error, $mailfile->errors, 'errors');
} else {
$result = $mailfile->sendfile();
}
if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
$conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
}
}
}
// Copy files into ticket directory
$destdir = $conf->ticket->dir_output.'/'.$object->ref;
if (!dol_is_dir($destdir)) {
dol_mkdir($destdir);
}
foreach ($filename as $i => $val) {
dol_move($filepath[$i], $destdir.'/'.$filename[$i], 0, 1);
$formmail->remove_attached_files($i);
}
//setEventMessages($langs->trans('YourTicketSuccessfullySaved'), null, 'mesgs');
// Make a redirect to avoid to have ticket submitted twice if we make back
$messagetoshow = $langs->trans('MesgInfosPublicTicketCreatedWithTrackId', '{s1}', '{s2}');
$messagetoshow = str_replace(array('{s1}', '{s2}'), array('<strong>'.$object->track_id.'</strong>', '<strong>'.$object->ref.'</strong>'), $messagetoshow);
setEventMessages($messagetoshow, null, 'warnings');
setEventMessages($langs->trans('PleaseRememberThisId'), null, 'warnings');
header("Location: index.php".(!empty($entity) && !empty($conf->multicompany->enabled)?'?entity='.$entity:''));
exit;
}
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
}
}
if (!$error) {
$this->results = array('myreturn' => 999);
$this->resprints = 'A text to show';
return 0; // or return 1 to replace standard code
} else {
$this->errors[] = 'Error message';
return -1;
}
}
}
I have two problems:
How do I get accurate information about my errors? Because my server or Dolibarr logs do not allow me to know exactly on which line my error occurs.
Why do I get a 500 error when I uncomment the line
$extrafields->fetch_name_optionals_label($object->table_element);
although “htdocs/core/class/extrafields.class.php” is included?
Thanks a lot for your help !