--- /tmp/dsg/dolibarr/htdocs/ticket/class/github_19.0.3_actions_ticket.class.php
+++ /tmp/dsg/dolibarr/htdocs/ticket/class/client_actions_ticket.class.php
@@ -4 +3,0 @@
- * Copyright (C) 2024      Destailleur Laurent <eldy@users.sourceforge.net>

@@ -30 +28,0 @@
-require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';

@@ -33,3 +30,0 @@
-require_once DOL_DOCUMENT_ROOT.'/core/class/commonhookactions.class.php';

-

-// TODO Only the last method emailElementlist is a hook method. Other must be moved into standard ticket.class.php

@@ -41 +36 @@
-class ActionsTicket extends CommonHookActions

+class ActionsTicket

@@ -48,3 +42,0 @@
-	/**

-	 * @var Ticket Ticket

-	 */

@@ -90 +81,0 @@
-

@@ -120 +111 @@
-	 * @return int              		Return integer <0 if KO, >0 if OK

+	 * @return 	void

@@ -150 +141 @@
-		$this->dao->fetch($id);

+		$this->dao->fetch($id, '', $track_id);

@@ -173 +164 @@
-			return $langs->trans("TicketAddMessage");

+			return $langs->trans("AddMessage");

@@ -192 +183 @@
-		if ($user->hasRight('ticket', 'manage') && $action == 'edit_message_init') {

+		if (!empty($user->rights->ticket->manage) && $action == 'edit_message_init') {

@@ -193,0 +185 @@
+

@@ -195 +187 @@
-			print '<input type="hidden" name="token" value="'.newToken().'">';

+			print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';

@@ -200,0 +193 @@
+		print '<div class="underbanner clearboth"></div>';

@@ -202,2 +195,2 @@
-		print '<table class="noborder centpercent margintable margintable">';

-		print '<tr class="liste_titre trforfield"><td class="nowrap titlefield">';

+		print '<table class="noborder centpercent margintable">';

+		print '<tr class="liste_titre"><td class="nowrap titlefield">';

@@ -206,2 +199,2 @@
-		if ($user->hasRight("ticket", "manage")) {

-			print '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=edit_message_init&token='.newToken().'&track_id='.$object->track_id.'">'.img_edit($langs->trans('Modify')).'</a>';

+		if ($user->rights->ticket->manage) {

+			print '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=edit_message_init&amp;track_id='.$object->track_id.'">'.img_edit($langs->trans('Modify')).'</a>';

@@ -213 +206 @@
-		if ($user->hasRight('ticket', 'manage') && $action == 'edit_message_init') {

+		if (!empty($user->rights->ticket->manage) && $action == 'edit_message_init') {

@@ -215 +208 @@
-			$msg = GETPOSTISSET('message_initial') ? GETPOST('message_initial', 'restricthtml') : $object->message;

+			$msg = GETPOST('message_initial', 'alpha') ? GETPOST('message_initial', 'alpha') : $object->message;

@@ -218,2 +211 @@
-			$ckeditorenabledforticket = getDolGlobalString('FCKEDITOR_ENABLE_TICKET');

-			$doleditor = new DolEditor('message_initial', $msg, '100%', 250, 'dolibarr_details', 'In', true, $uselocalbrowser, $ckeditorenabledforticket, ROWS_9, '95%');

+			$doleditor = new DolEditor('message_initial', $msg, '100%', 250, 'dolibarr_details', 'In', true, $uselocalbrowser, $conf->global->FCKEDITOR_ENABLE_TICKET, ROWS_4, '95%');

@@ -222,7 +214,6 @@
-			print '<div class="longmessagecut">';

-			//print dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($object->message), 1, 1, 1, 0)), 1, 1, 'common', 0, 1);

-			print dolPrintHTML($object->message);

-			print '</div>';

-			/*print '<div class="clear center">';

-			print $langs->trans("More").'...';

-			print '</div>';*/

+			// Deal with format differences (text / HTML)

+			if (dol_textishtml($object->message)) {

+				print $object->message;

+			} else {

+				print dol_nl2br($object->message);

+			}

@@ -232 +223 @@
-		if ($user->hasRight('ticket', 'manage') && $action == 'edit_message_init') {

+		if (!empty($user->rights->ticket->manage) && $action == 'edit_message_init') {

@@ -234,2 +225,2 @@
-			print ' <input type="submit" class="button button-edit" value="'.$langs->trans('Modify').'">';

-			print ' <input type="submit" class="button button-cancel" name="cancel" value="'.$langs->trans("Cancel").'">';

+			print ' <input type="submit" class="button" value="'.$langs->trans('Modify').'">';

+			print ' <input type="submit" class="button" name="cancel" value="'.$langs->trans('Cancel').'">';

@@ -243 +234 @@
-		if ($user->hasRight('ticket', 'manage') && $action == 'edit_message_init') {

+		if (!empty($user->rights->ticket->manage) && $action == 'edit_message_init') {

@@ -259 +250 @@
-		global $langs, $user;

+		global $conf, $langs, $user;

@@ -263,7 +254,4 @@
-		if ($ret < 0) {

-			dol_print_error($this->dao->db);

-		}

-

-		$action = GETPOST('action', 'aZ09');

-

-		print '<div class="ticketpublicarea ticketlargemargin centpercent" style="padding-top: 0">';

+		if ($ret < 0) dol_print_error($this->dao->db);

+

+		$action = GETPOST('action', 'alpha');

+

@@ -271,7 +259,4 @@
-		print '</div>';

-

-		if (is_array($this->dao->cache_msgs_ticket) && count($this->dao->cache_msgs_ticket) > 0) {

-			print '<div class="ticketpublicarea ticketlargemargin centpercent">';

-

-			print '<div class="div-table-responsive-no-min">';

-			print '<table class="border centpercent">';

+

+		if (is_array($this->dao->cache_msgs_ticket) && count($this->dao->cache_msgs_ticket) > 0)

+		{

+			print '<table class="border" style="width:100%;">';

@@ -299 +284 @@
-					print img_picto('', 'object_action', 'class="paddingright"').dol_print_date($arraymsgs['datep'], 'dayhour');

+					print img_picto('', 'object_action', 'class="paddingright"').dol_print_date($arraymsgs['datec'], 'dayhour');

@@ -308,8 +292,0 @@
-							}

-						} elseif (isset($arraymsgs['fk_contact_author'])) {

-							$contactstat = new Contact($this->db);

-							$res = $contactstat->fetch(0, null, '', $arraymsgs['fk_contact_author']);

-							if ($res) {

-								print $contactstat->getNomUrl(0, 'nolink');

-							} else {

-								print $arraymsgs['fk_contact_author'];

@@ -326,56 +302,0 @@
-

-					//attachment

-

-					$documents = array();

-

-					$sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id';

-					$sql .= ', ecm.filepath, ecm.filename, ecm.share';

-					$sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';

-					$sql .= " WHERE ecm.filepath = 'agenda/".$arraymsgs['id']."'";

-					$sql .= ' ORDER BY ecm.position ASC';

-

-					$resql = $this->db->query($sql);

-					if ($resql) {

-						if ($this->db->num_rows($resql)) {

-							while ($obj = $this->db->fetch_object($resql)) {

-								$documents[$obj->id] = $obj;

-							}

-						}

-					}

-					if (!empty($documents)) {

-						$isshared = 0;

-						$footer = '<div class="timeline-documents-container">';

-						foreach ($documents as $doc) {

-							if (!empty($doc->share)) {

-								$isshared = 1;

-								$footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';

-								$footer .= ' data-id="'.$doc->id.'" ';

-								$footer .= ' data-path="'.$doc->filepath.'"';

-								$footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';

-								$footer .= '>';

-

-								$filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;

-								$mime = dol_mimetype($filePath);

-								$thumb = $arraymsgs['id'].'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));

-								$doclink = DOL_URL_ROOT.'/document.php?hashp='.urlencode($doc->share);

-

-								$mimeAttr = ' mime="'.$mime.'" ';

-								$class = '';

-								if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {

-									$class .= ' documentpreview';

-								}

-

-								$footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank"  '.$mimeAttr.' >';

-								$footer .= img_mime($filePath).' '.$doc->filename;

-								$footer .= '</a>';

-

-								$footer .= '</span>';

-							}

-						}

-						$footer .= '</div>';

-						if ($isshared == 1) {

-							print '<br>';

-							print '<br>';

-							print $footer;

-						}

-					}

@@ -388,2 +308,0 @@
-			print '</div>';

-			print '</div>';

@@ -391 +309,0 @@
-			print '<div class="ticketpublicarea ticketlargemargin centpercent">';

@@ -393 +310,0 @@
-			print '</div>';

@@ -465 +382 @@
-		print '<div class="div-table-responsive-no-min margintoponly navBarForStatus">';

+		print '<div class="div-table-responsive-no-min margintoponly">';

@@ -475 +392 @@
-		foreach ($object->labelStatusShort as $status => $status_label) {

+		foreach ($object->statuts_short as $status => $status_label) {

@@ -479,4 +396,3 @@
-				if ($status == 1) {

-					$urlforbutton = $_SERVER['PHP_SELF'].'?track_id='.$object->track_id.'&action=set_read&token='.newToken(); // To set as read, we use a dedicated action

-				} else {

-					$urlforbutton = $_SERVER['PHP_SELF'].'?track_id='.$object->track_id.'&action=confirm_set_status&token='.newToken().'&new_status='.((int) $status);

+				if ($status == 1)

+				{

+					$urlforbutton = $_SERVER['PHP_SELF'].'?track_id='.$object->track_id.'&action=mark_ticket_read'; // To set as read, we use a dedicated action

@@ -484,5 +400,7 @@
-

-				print '<a class="butAction butStatus marginbottomonly" href="'.$urlforbutton.'">';

-				print $object->LibStatut($status, 3, 1).' ';

-				//print img_picto($langs->trans($object->labelStatusShort[$status]), 'statut'.$status.'.png@ticket', '', false, 0, 0, '', 'valignmiddle').' ';

-				print $langs->trans($object->labelStatusShort[$status]);

+				else

+				{

+					$urlforbutton = $_SERVER['PHP_SELF'].'?track_id='.$object->track_id.'&action=set_status&new_status='.$status;

+				}

+

+				print '<a class="butAction buttonticket marginbottomonly" href="'.$urlforbutton.'">';

+				print img_picto($langs->trans($object->statuts_short[$status]), 'statut'.$status.'.png@ticket').' '.$langs->trans($object->statuts_short[$status]);

@@ -493,3 +411 @@
-		print '</div>';

-		print '</div>';

-		print '<br>';

+		print '</div></div><br>';

--- /tmp/dsg/dolibarr/htdocs/ticket/class/github_19.0.3_api_tickets.class.php
+++ /tmp/dsg/dolibarr/htdocs/ticket/class/client_api_tickets.class.php
@@ -20 +20 @@
-require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';

+require 'ticket.class.php';

@@ -32,532 +32,539 @@
-	/**

-	 * @var array   $FIELDS     Mandatory fields, checked when create and update object

-	 */

-	public static $FIELDS = array(

-		'subject',

-		'message'

-	);

-

-	/**

-	 * @var array   $FIELDS_MESSAGES     Mandatory fields, checked when create and update object

-	 */

-	public static $FIELDS_MESSAGES = array(

-		'track_id',

-		'message'

-	);

-

-	/**

-	 * @var Ticket $ticket {@type Ticket}

-	 */

-	public $ticket;

-

-	/**

-	 * Constructor

-	 */

-	public function __construct()

-	{

-		global $db;

-		$this->db = $db;

-		$this->ticket = new Ticket($this->db);

-	}

-

-	/**

-	 * Get properties of a Ticket object.

-	 *

-	 * Return an array with ticket informations

-	 *

-	 * @param	int				$id			ID of ticket

-	 * @return  Object						Object with cleaned properties

-	 *

-	 * @throws RestException 401

-	 * @throws RestException 403

-	 * @throws RestException 404

-	 */

-	public function get($id)

-	{

-		return $this->getCommon($id, '', '');

-	}

-

-	/**

-	 * Get properties of a Ticket object from track id

-	 *

-	 * Return an array with ticket informations

-	 *

-	 * @param	string			$track_id	Tracking ID of ticket

-	 * @return	array|mixed					Data without useless information

-	 *

-	 * @url GET track_id/{track_id}

-	 *

-	 * @throws RestException	401

-	 * @throws RestException	403

-	 * @throws RestException	404

-	 */

-	public function getByTrackId($track_id)

-	{

-		return $this->getCommon(0, $track_id, '');

-	}

-

-	/**

-	 * Get properties of a Ticket object from ref

-	 *

-	 * Return an array with ticket informations

-	 *

-	 * @param	string			$ref		Reference for ticket

-	 * @return	array|mixed					Data without useless information

-	 *

-	 * @url GET ref/{ref}

-	 *

-	 * @throws RestException 401

-	 * @throws RestException 403

-	 * @throws RestException 404

-	 */

-	public function getByRef($ref)

-	{

-		return $this->getCommon(0, '', $ref);

-	}

-

-	/**

-	 * Get properties of a Ticket object

-	 * Return an array with ticket informations

-	 *

-	 * @param	int				$id			ID of ticket

-	 * @param	string			$track_id	Tracking ID of ticket

-	 * @param	string			$ref		Reference for ticket

-	 * @return	array|mixed					Data without useless information

-	 */

-	private function getCommon($id = 0, $track_id = '', $ref = '')

-	{

-		if (!DolibarrApiAccess::$user->rights->ticket->read) {

-			throw new RestException(403);

-		}

-

-		// Check parameters

-		if (($id < 0) && !$track_id && !$ref) {

-			throw new RestException(401, 'Wrong parameters');

-		}

-		if ($id == 0) {

-			$result = $this->ticket->initAsSpecimen();

-		} else {

-			$result = $this->ticket->fetch($id, $ref, $track_id);

-		}

-		if (!$result) {

-			throw new RestException(404, 'Ticket not found');

-		}

-

-		// String for user assigned

-		if ($this->ticket->fk_user_assign > 0) {

-			$userStatic = new User($this->db);

-			$userStatic->fetch($this->ticket->fk_user_assign);

-			$this->ticket->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;

-		}

-

-		// Messages of ticket

-		$messages = array();

-		$this->ticket->loadCacheMsgsTicket();

-		if (is_array($this->ticket->cache_msgs_ticket) && count($this->ticket->cache_msgs_ticket) > 0) {

-			$num = count($this->ticket->cache_msgs_ticket);

-			$i = 0;

-			while ($i < $num) {

-				if ($this->ticket->cache_msgs_ticket[$i]['fk_user_author'] > 0) {

-					$user_action = new User($this->db);

-					$user_action->fetch($this->ticket->cache_msgs_ticket[$i]['fk_user_author']);

-				}

-

-				// Now define messages

-				$messages[] = array(

-				'id' => $this->ticket->cache_msgs_ticket[$i]['id'],

-				'fk_user_action' => $this->ticket->cache_msgs_ticket[$i]['fk_user_author'],

-				'fk_user_action_socid' =>  $user_action->socid,

-				'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname),

-				'message' => $this->ticket->cache_msgs_ticket[$i]['message'],

-				'datec' => $this->ticket->cache_msgs_ticket[$i]['datec'],

-				'private' => $this->ticket->cache_msgs_ticket[$i]['private']

-				);

-				$i++;

-			}

-			$this->ticket->messages = $messages;

-		}

-

-		if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

-			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

-		}

-		return $this->_cleanObjectDatas($this->ticket);

-	}

-

-	/**

-	 * List tickets

-	 *

-	 * Get a list of tickets

-	 *

-	 * @param int       $socid      Filter list with thirdparty ID

-	 * @param string	$sortfield	Sort field

-	 * @param string	$sortorder	Sort order

-	 * @param int		$limit		Limit for list

-	 * @param int		$page		Page number

-	 * @param string	$sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101') and (t.fk_statut:=:1)"

-	 * @param string    $properties	Restrict the data returned to theses properties. Ignored if empty. Comma separated list of properties names

-	 *

-	 * @return array Array of ticket objects

-	 *

-	 */

-	public function index($socid = 0, $sortfield = "t.rowid", $sortorder = "ASC", $limit = 100, $page = 0, $sqlfilters = '', $properties = '')

-	{

-		global $db, $conf;

-

-		if (!DolibarrApiAccess::$user->rights->ticket->read) {

-			throw new RestException(403);

-		}

-

-		$obj_ret = array();

-

-		if (!$socid && DolibarrApiAccess::$user->socid) {

-			$socid = DolibarrApiAccess::$user->socid;

-		}

-

-		$search_sale = null;

-		// If the internal user must only see his customers, force searching by him

-		$search_sale = 0;

-		if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) {

-			$search_sale = DolibarrApiAccess::$user->id;

-		}

-

-		$sql = "SELECT t.rowid";

-		if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

-			$sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)

-		}

-		$sql .= " FROM ".MAIN_DB_PREFIX."ticket AS t LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields

-

-		if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

-			$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale

-		}

-

-		$sql .= ' WHERE t.entity IN ('.getEntity('ticket', 1).')';

-		if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

-			$sql .= " AND t.fk_soc = sc.fk_soc";

-		}

-		if ($socid > 0) {

-			$sql .= " AND t.fk_soc = ".((int) $socid);

-		}

-		if ($search_sale > 0) {

-			$sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale

-		}

-

-		// Insert sale filter

-		if ($search_sale > 0) {

-			$sql .= " AND sc.fk_user = ".((int) $search_sale);

-		}

-		// Add sql filters

-		if ($sqlfilters) {

-			$errormessage = '';

-			$sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);

-			if ($errormessage) {

-				throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);

-			}

-		}

-

-		$sql .= $this->db->order($sortfield, $sortorder);

-

-		if ($limit) {

-			if ($page < 0) {

-				$page = 0;

-			}

-			$offset = $limit * $page;

-

-			$sql .= $this->db->plimit($limit, $offset);

-		}

-

-		$result = $this->db->query($sql);

-		if ($result) {

-			$num = $this->db->num_rows($result);

-			$i = 0;

-			while ($i < $num) {

-				$obj = $this->db->fetch_object($result);

-				$ticket_static = new Ticket($this->db);

-				if ($ticket_static->fetch($obj->rowid)) {

-					if ($ticket_static->fk_user_assign > 0) {

-						$userStatic = new User($this->db);

-						$userStatic->fetch($ticket_static->fk_user_assign);

-						$ticket_static->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;

-					}

-					$obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($ticket_static), $properties);

-				}

-				$i++;

-			}

-		} else {

-			throw new RestException(503, 'Error when retrieve ticket list');

-		}

-

-		return $obj_ret;

-	}

-

-	/**

-	 * Create ticket object

-	 *

-	 * @param array $request_data   Request datas

-	 * @return int  ID of ticket

-	 */

-	public function post($request_data = null)

-	{

-		$ticketstatic = new Ticket($this->db);

-		if (!DolibarrApiAccess::$user->rights->ticket->write) {

-			throw new RestException(401);

-		}

-		// Check mandatory fields

-		$result = $this->_validate($request_data);

-

-		foreach ($request_data as $field => $value) {

-			if ($field === 'caller') {

-				// Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again whith the caller

-				$this->ticket->context['caller'] = $request_data['caller'];

-				continue;

-			}

-

-			$this->ticket->$field = $value;

-		}

-		if (empty($this->ticket->ref)) {

-			$this->ticket->ref = $ticketstatic->getDefaultRef();

-		}

-		if (empty($this->ticket->track_id)) {

-			$this->ticket->track_id = generate_random_id(16);

-		}

-

-		if ($this->ticket->create(DolibarrApiAccess::$user) < 0) {

-			throw new RestException(500, "Error creating ticket", array_merge(array($this->ticket->error), $this->ticket->errors));

-		}

-

-		return $this->ticket->id;

-	}

-

-	/**

-	 * Create ticket object

-	 *

-	 * @param array $request_data   Request datas

-	 * @return int  ID of ticket

-	 *

-	 */

-	public function postNewMessage($request_data = null)

-	{

-		$ticketstatic = new Ticket($this->db);

-		if (!DolibarrApiAccess::$user->rights->ticket->write) {

-			throw new RestException(401);

-		}

-		// Check mandatory fields

-		$result = $this->_validateMessage($request_data);

-

-		foreach ($request_data as $field => $value) {

-			if ($field === 'caller') {

-				// Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again whith the caller

-				$this->ticket->context['caller'] = $request_data['caller'];

-				continue;

-			}

-

-			$this->ticket->$field = $value;

-		}

-		$ticketMessageText = $this->ticket->message;

-		$result = $this->ticket->fetch('', '', $this->ticket->track_id);

-		if (!$result) {

-			throw new RestException(404, 'Ticket not found');

-		}

-		$this->ticket->message = $ticketMessageText;

-		if (!$this->ticket->createTicketMessage(DolibarrApiAccess::$user)) {

-			throw new RestException(500, 'Error when creating ticket');

-		}

-		return $this->ticket->id;

-	}

-

-	/**

-	 * Update ticket

-	 *

-	 * @param int   $id             Id of ticket to update

-	 * @param array $request_data   Datas

-	 * @return int

-	 *

-	 */

-	public function put($id, $request_data = null)

-	{

-		if (!DolibarrApiAccess::$user->rights->ticket->write) {

-			throw new RestException(401);

-		}

-

-		$result = $this->ticket->fetch($id);

-		if (!$result) {

-			throw new RestException(404, 'Ticket not found');

-		}

-

-		if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

-			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

-		}

-

-		foreach ($request_data as $field => $value) {

-			if ($field === 'caller') {

-				// Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again whith the caller

-				$this->ticket->context['caller'] = $request_data['caller'];

-				continue;

-			}

-

-			$this->ticket->$field = $value;

-		}

-

-		if ($this->ticket->update(DolibarrApiAccess::$user)) {

-			return $this->get($id);

-		}

-

-		return false;

-	}

-

-	/**

-	 * Delete ticket

-	 *

-	 * @param   int     $id   Ticket ID

-	 * @return  array

-	 *

-	 */

-	public function delete($id)

-	{

-		if (!DolibarrApiAccess::$user->rights->ticket->delete) {

-			throw new RestException(401);

-		}

-		$result = $this->ticket->fetch($id);

-		if (!$result) {

-			throw new RestException(404, 'Ticket not found');

-		}

-

-		if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

-			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

-		}

-

-		if (!$this->ticket->delete($id)) {

-			throw new RestException(500, 'Error when deleting ticket');

-		}

-

-		return array(

-			'success' => array(

-				'code' => 200,

-				'message' => 'Ticket deleted'

-			)

-		);

-	}

-

-	/**

-	 * Validate fields before create or update object

-	 *

-	 * @param array $data   Data to validate

-	 * @return array

-	 *

-	 * @throws RestException

-	 */

-	private function _validate($data)

-	{

-		$ticket = array();

-		foreach (Tickets::$FIELDS as $field) {

-			if (!isset($data[$field])) {

-				throw new RestException(400, "$field field missing");

-			}

-			$ticket[$field] = $data[$field];

-		}

-		return $ticket;

-	}

-

-	/**

-	 * Validate fields before create or update object message

-	 *

-	 * @param array $data   Data to validate

-	 * @return array

-	 *

-	 * @throws RestException

-	 */

-	private function _validateMessage($data)

-	{

-		$ticket = array();

-		foreach (Tickets::$FIELDS_MESSAGES as $field) {

-			if (!isset($data[$field])) {

-				throw new RestException(400, "$field field missing");

-			}

-			$ticket[$field] = $data[$field];

-		}

-		return $ticket;

-	}

-

-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore

-	/**

-	 * Clean sensible object datas

-	 *

-	 * @param   Object  $object     Object to clean

-	 * @return  Object              Object with cleaned properties

-	 *

-	 * @todo use an array for properties to clean

-	 *

-	 */

-	protected function _cleanObjectDatas($object)

-	{

-		// phpcs:enable

-		$object = parent::_cleanObjectDatas($object);

-

-		// Other attributes to clean

-		$attr2clean = array(

-			"contact",

-			"contact_id",

-			"ref_previous",

-			"ref_next",

-			"ref_ext",

-			"table_element_line",

-			"statut",

-			"country",

-			"country_id",

-			"country_code",

-			"barcode_type",

-			"barcode_type_code",

-			"barcode_type_label",

-			"barcode_type_coder",

-			"mode_reglement_id",

-			"cond_reglement_id",

-			"cond_reglement",

-			"fk_delivery_address",

-			"shipping_method_id",

-			"modelpdf",

-			"fk_account",

-			"note_public",

-			"note_private",

-			"note",

-			"total_ht",

-			"total_tva",

-			"total_localtax1",

-			"total_localtax2",

-			"total_ttc",

-			"fk_incoterms",

-			"label_incoterms",

-			"location_incoterms",

-			"name",

-			"lastname",

-			"firstname",

-			"civility_id",

-			"canvas",

-			"cache_msgs_ticket",

-			"cache_logs_ticket",

-			"cache_types_tickets",

-			"cache_category_tickets",

-			"regeximgext",

-			"labelStatus",

-			"labelStatusShort"

-		);

-		foreach ($attr2clean as $toclean) {

-			unset($object->$toclean);

-		}

-

-		// If object has lines, remove $db property

-		if (isset($object->lines) && count($object->lines) > 0) {

-			$nboflines = count($object->lines);

-			for ($i = 0; $i < $nboflines; $i++) {

-				$this->_cleanObjectDatas($object->lines[$i]);

-			}

-		}

-

-		// If object has linked objects, remove $db property

-		if (isset($object->linkedObjects) && count($object->linkedObjects) > 0) {

-			foreach ($object->linkedObjects as $type_object => $linked_object) {

-				foreach ($linked_object as $object2clean) {

-					$this->_cleanObjectDatas($object2clean);

-				}

-			}

-		}

-		return $object;

-	}

+    /**

+     * @var array   $FIELDS     Mandatory fields, checked when create and update object

+     */

+    public static $FIELDS = array(

+        'subject',

+        'message'

+    );

+

+    /**

+     * @var array   $FIELDS_MESSAGES     Mandatory fields, checked when create and update object

+     */

+    public static $FIELDS_MESSAGES = array(

+        'track_id',

+        'message'

+    );

+

+    /**

+     * @var Ticket $ticket {@type Ticket}

+     */

+    public $ticket;

+

+    /**

+     * Constructor

+     */

+    public function __construct()

+    {

+        global $db;

+        $this->db = $db;

+        $this->ticket = new Ticket($this->db);

+    }

+

+    /**

+     * Get properties of a Ticket object.

+     *

+     * Return an array with ticket informations

+     *

+     * @param	int 			$id 		ID of ticket

+     * @return 	array|mixed 				Data without useless information

+     *

+     * @throws RestException 401

+     * @throws RestException 403

+     * @throws RestException 404

+     */

+    public function get($id)

+    {

+        return $this->getCommon($id, '', '');

+    }

+

+    /**

+     * Get properties of a Ticket object from track id

+     *

+     * Return an array with ticket informations

+     *

+     * @param	string  		$track_id 	Tracking ID of ticket

+     * @return 	array|mixed 				Data without useless information

+     *

+     * @url GET track_id/{track_id}

+     *

+     * @throws RestException 	401

+     * @throws RestException 	403

+     * @throws RestException 	404

+     */

+    public function getByTrackId($track_id)

+    {

+        return $this->getCommon(0, $track_id, '');

+    }

+

+    /**

+     * Get properties of a Ticket object from ref

+     *

+     * Return an array with ticket informations

+     *

+     * @param	string  		$ref    	Reference for ticket

+     * @return 	array|mixed 				Data without useless information

+     *

+     * @url GET ref/{ref}

+     *

+     * @throws RestException 401

+     * @throws RestException 403

+     * @throws RestException 404

+     */

+    public function getByRef($ref)

+    {

+        try {

+            return $this->getCommon(0, '', $ref);

+        }

+        catch (Exception $e)

+        {

+               throw $e;

+        }

+    }

+

+    /**

+     * Get properties of a Ticket object

+     * Return an array with ticket informations

+     *

+     * @param	int 			$id 		ID of ticket

+     * @param	string  		$track_id 	Tracking ID of ticket

+     * @param	string  		$ref    	Reference for ticket

+     * @return 	array|mixed 				Data without useless information

+     */

+    private function getCommon($id = 0, $track_id = '', $ref = '')

+    {

+        if (!DolibarrApiAccess::$user->rights->ticket->read) {

+            throw new RestException(403);

+        }

+

+        // Check parameters

+        if (!$id && !$track_id && !$ref) {

+            throw new RestException(401, 'Wrong parameters');

+        }

+

+        $result = $this->ticket->fetch($id, $ref, $track_id);

+        if (!$result) {

+            throw new RestException(404, 'Ticket not found');

+        }

+

+        // String for user assigned

+        if ($this->ticket->fk_user_assign > 0) {

+            $userStatic = new User($this->db);

+            $userStatic->fetch($this->ticket->fk_user_assign);

+            $this->ticket->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;

+        }

+

+        // Messages of ticket

+        $messages = array();

+        $this->ticket->loadCacheMsgsTicket();

+        if (is_array($this->ticket->cache_msgs_ticket) && count($this->ticket->cache_msgs_ticket) > 0) {

+            $num = count($this->ticket->cache_msgs_ticket);

+            $i = 0;

+            while ($i < $num) {

+                if ($this->ticket->cache_msgs_ticket[$i]['fk_user_author'] > 0) {

+                    $user_action = new User($this->db);

+                    $user_action->fetch($this->ticket->cache_msgs_ticket[$i]['fk_user_author']);

+                }

+

+                // Now define messages

+                $messages[] = array(

+                'id' => $this->ticket->cache_msgs_ticket[$i]['id'],

+                'fk_user_action' => $this->ticket->cache_msgs_ticket[$i]['fk_user_author'],

+                'fk_user_action_socid' =>  $user_action->socid,

+                'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname),

+                'message' => $this->ticket->cache_msgs_ticket[$i]['message'],

+                'datec' => $this->ticket->cache_msgs_ticket[$i]['datec'],

+                'private' => $this->ticket->cache_msgs_ticket[$i]['private']

+                );

+                $i++;

+            }

+            $this->ticket->messages = $messages;

+        }

+

+        // History

+        $history = array();

+        $this->ticket->loadCacheLogsTicket();

+        if (is_array($this->ticket->cache_logs_ticket) && count($this->ticket->cache_logs_ticket) > 0) {

+            $num = count($this->ticket->cache_logs_ticket);

+            $i = 0;

+            while ($i < $num) {

+                if ($this->ticket->cache_logs_ticket[$i]['fk_user_create'] > 0) {

+                    $user_action = new User($this->db);

+                    $user_action->fetch($this->ticket->cache_logs_ticket[$i]['fk_user_create']);

+                }

+

+                // Now define messages

+                $history[] = array(

+                'id' => $this->ticket->cache_logs_ticket[$i]['id'],

+                'fk_user_author' => $this->ticket->cache_msgs_ticket[$i]['fk_user_author'],

+                'fk_user_action' => $this->ticket->cache_logs_ticket[$i]['fk_user_create'],

+                'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname),

+                'message' => $this->ticket->cache_logs_ticket[$i]['message'],

+                'datec' => $this->ticket->cache_logs_ticket[$i]['datec'],

+                );

+                $i++;

+            }

+            $this->ticket->history = $history;

+        }

+

+

+        if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

+            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

+        }

+        return $this->_cleanObjectDatas($this->ticket);

+    }

+

+    /**

+     * List tickets

+     *

+     * Get a list of tickets

+     *

+     * @param int       $socid      Filter list with thirdparty ID

+     * @param string	$sortfield	Sort field

+     * @param string	$sortorder	Sort order

+     * @param int		$limit		Limit for list

+     * @param int		$page		Page number

+     * @param string	$sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101') and (t.fk_statut:=:1)"

+     *

+     * @return array Array of ticket objects

+     *

+     */

+    public function index($socid = 0, $sortfield = "t.rowid", $sortorder = "ASC", $limit = 100, $page = 0, $sqlfilters = '')

+    {

+        global $db, $conf;

+

+        $obj_ret = array();

+

+        if (!$socid && DolibarrApiAccess::$user->socid) {

+            $socid = DolibarrApiAccess::$user->socid;

+        }

+

+        // If the internal user must only see his customers, force searching by him

+        if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) {

+            $search_sale = DolibarrApiAccess::$user->id;

+        }

+

+        $sql = "SELECT t.rowid";

+        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

+            $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)

+        }

+        $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";

+

+        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

+            $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale

+        }

+

+        $sql .= ' WHERE t.entity IN ('.getEntity('ticket', 1).')';

+        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {

+            $sql .= " AND t.fk_soc = sc.fk_soc";

+        }

+        if ($socid > 0) {

+            $sql .= " AND t.fk_soc = ".$socid;

+        }

+        if ($search_sale > 0) {

+            $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale

+        }

+

+        // Insert sale filter

+        if ($search_sale > 0) {

+            $sql .= " AND sc.fk_user = ".$search_sale;

+        }

+        // Add sql filters

+        if ($sqlfilters) {

+            if (!DolibarrApi::_checkFilters($sqlfilters)) {

+                throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);

+            }

+            $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';

+            $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";

+        }

+

+        $sql .= $db->order($sortfield, $sortorder);

+

+        if ($limit) {

+            if ($page < 0) {

+                $page = 0;

+            }

+            $offset = $limit * $page;

+

+            $sql .= $this->db->plimit($limit, $offset);

+        }

+

+        $result = $db->query($sql);

+        if ($result) {

+            $num = $db->num_rows($result);

+            $i = 0;

+            while ($i < $num) {

+                $obj = $db->fetch_object($result);

+                $ticket_static = new Ticket($db);

+                if ($ticket_static->fetch($obj->rowid)) {

+                    if ($ticket_static->fk_user_assign > 0) {

+                        $userStatic = new User($this->db);

+                        $userStatic->fetch($ticket_static->fk_user_assign);

+                        $ticket_static->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;

+                    }

+                    $obj_ret[] = $this->_cleanObjectDatas($ticket_static);

+                }

+                $i++;

+            }

+        } else {

+            throw new RestException(503, 'Error when retrieve ticket list');

+        }

+        if (!count($obj_ret)) {

+            throw new RestException(404, 'No ticket found');

+        }

+            return $obj_ret;

+    }

+

+    /**

+     * Create ticket object

+     *

+     * @param array $request_data   Request datas

+     * @return int  ID of ticket

+     */

+    public function post($request_data = null)

+    {

+        $ticketstatic = new Ticket($this->db);

+        if (!DolibarrApiAccess::$user->rights->ticket->write) {

+            throw new RestException(401);

+        }

+        // Check mandatory fields

+        $result = $this->_validate($request_data);

+

+        foreach ($request_data as $field => $value) {

+            $this->ticket->$field = $value;

+        }

+        if (empty($this->ticket->ref)) {

+            $this->ticket->ref = $ticketstatic->getDefaultRef();

+        }

+        if (empty($this->ticket->track_id)) {

+            $this->ticket->track_id = generate_random_id(16);

+        }

+

+        if ($this->ticket->create(DolibarrApiAccess::$user) < 0) {

+        	throw new RestException(500, "Error creating ticket", array_merge(array($this->ticket->error), $this->ticket->errors));

+        }

+

+        return $this->ticket->id;

+    }

+

+    /**

+     * Create ticket object

+     *

+     * @param array $request_data   Request datas

+     * @return int  ID of ticket

+     *

+     */

+    public function postNewMessage($request_data = null)

+    {

+        $ticketstatic = new Ticket($this->db);

+        if (!DolibarrApiAccess::$user->rights->ticket->write) {

+            throw new RestException(401);

+        }

+        // Check mandatory fields

+        $result = $this->_validateMessage($request_data);

+

+        foreach ($request_data as $field => $value) {

+            $this->ticket->$field = $value;

+        }

+        $ticketMessageText = $this->ticket->message;

+        $result = $this->ticket->fetch('', '', $this->ticket->track_id);

+        if (!$result) {

+            throw new RestException(404, 'Ticket not found');

+        }

+        $this->ticket->message = $ticketMessageText;

+        if (!$this->ticket->createTicketMessage(DolibarrApiAccess::$user)) {

+            throw new RestException(500);

+        }

+        return $this->ticket->id;

+    }

+

+    /**

+     * Update ticket

+     *

+     * @param int   $id             Id of ticket to update

+     * @param array $request_data   Datas

+     * @return int

+     *

+     */

+    public function put($id, $request_data = null)

+    {

+        if (!DolibarrApiAccess::$user->rights->ticket->write) {

+            throw new RestException(401);

+        }

+

+        $result = $this->ticket->fetch($id);

+        if (!$result) {

+            throw new RestException(404, 'Ticket not found');

+        }

+

+        if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

+            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

+        }

+

+        foreach ($request_data as $field => $value) {

+            $this->ticket->$field = $value;

+        }

+

+        if ($this->ticket->update($id, DolibarrApiAccess::$user)) {

+            return $this->get($id);

+        }

+

+        return false;

+    }

+

+    /**

+     * Delete ticket

+     *

+     * @param   int     $id   Ticket ID

+     * @return  array

+     *

+     */

+    public function delete($id)

+    {

+        if (!DolibarrApiAccess::$user->rights->ticket->delete) {

+            throw new RestException(401);

+        }

+        $result = $this->ticket->fetch($id);

+        if (!$result) {

+            throw new RestException(404, 'Ticket not found');

+        }

+

+        if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {

+            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);

+        }

+

+        if (!$this->ticket->delete($id)) {

+            throw new RestException(500);

+        }

+

+        return array(

+            'success' => array(

+                'code' => 200,

+                'message' => 'Ticket deleted'

+            )

+        );

+    }

+

+    /**

+     * Validate fields before create or update object

+     *

+     * @param array $data   Data to validate

+     * @return array

+     *

+     * @throws RestException

+     */

+    private function _validate($data)

+    {

+        $ticket = array();

+        foreach (Tickets::$FIELDS as $field) {

+            if (!isset($data[$field])) {

+                throw new RestException(400, "$field field missing");

+            }

+            $ticket[$field] = $data[$field];

+        }

+        return $ticket;

+    }

+

+    /**

+     * Validate fields before create or update object message

+     *

+     * @param array $data   Data to validate

+     * @return array

+     *

+     * @throws RestException

+     */

+    private function _validateMessage($data)

+    {

+        $ticket = array();

+        foreach (Tickets::$FIELDS_MESSAGES as $field) {

+            if (!isset($data[$field])) {

+                throw new RestException(400, "$field field missing");

+            }

+            $ticket[$field] = $data[$field];

+        }

+        return $ticket;

+    }

+

+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore

+    /**

+     * Clean sensible object datas

+     *

+     * @param   object  $object	Object to clean

+     * @return	array	Array of cleaned object properties

+     *

+     * @todo use an array for properties to clean

+     *

+     */

+    protected function _cleanObjectDatas($object)

+    {

+        // phpcs:enable

+        $object = parent::_cleanObjectDatas($object);

+

+        // Other attributes to clean

+        $attr2clean = array(

+            "contact",

+            "contact_id",

+            "ref_previous",

+            "ref_next",

+            "ref_ext",

+            "table_element_line",

+            "statut",

+            "country",

+            "country_id",

+            "country_code",

+            "barcode_type",

+            "barcode_type_code",

+            "barcode_type_label",

+            "barcode_type_coder",

+            "mode_reglement_id",

+            "cond_reglement_id",

+            "cond_reglement",

+            "fk_delivery_address",

+            "shipping_method_id",

+            "modelpdf",

+            "fk_account",

+            "note_public",

+            "note_private",

+            "note",

+            "total_ht",

+            "total_tva",

+            "total_localtax1",

+            "total_localtax2",

+            "total_ttc",

+            "fk_incoterms",

+            "label_incoterms",

+            "location_incoterms",

+            "name",

+            "lastname",

+            "firstname",

+            "civility_id",

+        	"canvas",

+            "cache_msgs_ticket",

+            "cache_logs_ticket",

+        	"cache_types_tickets",

+        	"cache_category_tickets",

+        	"regeximgext",

+            "statuts_short",

+            "statuts"

+        );

+        foreach ($attr2clean as $toclean) {

+            unset($object->$toclean);

+        }

+

+        // If object has lines, remove $db property

+        if (isset($object->lines) && count($object->lines) > 0) {

+            $nboflines = count($object->lines);

+            for ($i = 0; $i < $nboflines; $i++) {

+                $this->_cleanObjectDatas($object->lines[$i]);

+            }

+        }

+

+        // If object has linked objects, remove $db property

+        if (isset($object->linkedObjects) && count($object->linkedObjects) > 0) {

+            foreach ($object->linkedObjects as $type_object => $linked_object) {

+                foreach ($linked_object as $object2clean) {

+                    $this->_cleanObjectDatas($object2clean);

+                }

+            }

+        }

+        return $object;

+    }

--- /tmp/dsg/dolibarr/htdocs/ticket/class/github_19.0.3_ticket.class.php
+++ /tmp/dsg/dolibarr/htdocs/ticket/class/client_ticket.class.php
@@ -4,5 +4 @@
- * Copyright (C) 2019-2023 Frédéric France     <frederic.france@netlogic.fr>

- * Copyright (C) 2020      Laurent Destailleur <eldy@users.sourceforge.net>

- * Copyright (C) 2023      Charlene Benke 	   <charlene@patas-monkey.com>

- * Copyright (C) 2023	   Benjamin Falière	   <benjamin.faliere@altairis.fr>

- * Copyright (C) 2024		William Mead		<william.mead@manchenumerique.fr>

+ * Copyright (C) 2019       Frédéric France     <frederic.france@netlogic.fr>

@@ -42,5 +37,0 @@
-	 * @var DoliDb Database handler

-	 */

-	public $db;

-

-	/**

@@ -86 +76,0 @@
-	public $socid;

@@ -94,5 +83,0 @@
-	 * @var int Contract ID

-	 */

-	public $fk_contract;

-

-	/**

@@ -124,5 +108,0 @@
-	 * @var string Private message

-	 */

-	public $private;

-

-	/**

@@ -130,8 +110,2 @@
-	 * @deprecated use status

-	 * @see $status

-	 */

-	public $fk_statut;

-

-	/**

-	 * @var int  Ticket status

-	 */

+	 */

+	public $fk_statut;				// deprecated

@@ -171,21 +145 @@
-	 * Type label

-	 */

-	public $type_label;

-

-	/**

-	 * Category label

-	 */

-	public $category_label;

-

-	/**

-	 * Severity label

-	 */

-	public $severity_label;

-

-	/**

-	 * Email from user

-	 */

-	public $email_from;

-

-	/**

-	 * @var int Creation date

+	 * @var int Création date

@@ -196,5 +149,0 @@
-	 * @var int Last modification date

-	 */

-	public $tms = '';

-

-	/**

@@ -206,5 +154,0 @@
-	 * @var int Last message date

-	 */

-	public $date_last_msg_sent = '';

-

-	/**

@@ -226,21 +170 @@
-	 * @var array tickets severity

-	 */

-	public $cache_severity_tickets;

-

-	/**

-	 * @var array cache msgs ticket

-	 */

-	public $cache_msgs_ticket;

-

-	/**

-	 * @var array status labels

-	 */

-	public $labelStatus;

-

-	/**

-	 * @var array status short labels

-	 */

-	public $labelStatusShort;

-

-	/**

-	 * @var int Notify thirdparty at create

+	 * @var int Notify tiers at create

@@ -250,23 +173,0 @@
-	/**

-	 * @var string 	Email MSGID

-	 */

-	public $email_msgid;

-

-	/**

-	 * @var string 	Email Date

-	 */

-	public $email_date;

-

-	/**

-	 * @var string 	IP address

-	 */

-	public $ip;

-

-	/**

-	 * @var Ticket $oldcopy  State of this ticket as it was stored before an update operation (for triggers)

-	 */

-	public $oldcopy;

-

-	/**

-	 * @var Ticket[] array of Tickets

-	 */

@@ -275 +175,0 @@
-

@@ -279 +179,28 @@
-	public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php

+	public $regeximgext = '\.jpg|\.jpeg|\.bmp|\.gif|\.png|\.tiff';

+

+	public $fields = array(

+		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'position'=>1, 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),

+		'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),

+		'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>''),

+		'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),

+		'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>15, 'notnull'=>1, 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),

+		'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>'tdoverflowmax150'),

+		'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth75', 'autofocusoncreate'=>1),

+		'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth100'),

+		'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketGroup', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),

+		'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),

+		'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToThirparty", 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),

+		'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),

+		'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>1, 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"),

+		'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""),

+		'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1),

+		'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>1, 'enabled'=>1, 'position'=>501, 'notnull'=>1),

+		'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1),

+		'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),

+		'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),

+		'message' => array('type'=>'text', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),

+		'progress' => array('type'=>'varchar(100)', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>"", 'isameasure'=>1),

+		'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>1, 'position'=>550, 'notnull'=>1),

+		'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted')),

+		'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),

+	);

@@ -288,64 +215,4 @@
-	const STATUS_NEED_MORE_INFO = 5;	// waiting requester feedback

-	const STATUS_WAITING = 7;			// on hold

-	const STATUS_CLOSED = 8;			// Closed - Solved

-	const STATUS_CANCELED = 9;			// Closed - Not solved

-

-

-	/**

-	 *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')

-	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"

-	 *  'label' the translation key.

-	 *  'picto' is code of a picto to show before value in forms

-	 *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalString('MY_SETUP_PARAM'))

-	 *  'position' is the sort order of field.

-	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).

-	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)

-	 *  'noteditable' says if field is not editable (1 or 0)

-	 *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.

-	 *  'index' if we want an index in database.

-	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).

-	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.

-	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).

-	 *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'maxwidth200', 'wordbreak', 'tdoverflowmax200'

-	 *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.

-	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record

-	 *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.

-	 *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")

-	 *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.

-	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.

-	 *

-	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.

-	 */

-

-	// BEGIN MODULEBUILDER PROPERTIES

-	public $fields = array(

-		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),

-		'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),

-		'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>'', 'showoncombobox'=>1),

-		'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),

-		'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>15, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),

-		'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'csslist'=>'tdoverflowmax150'),

-		'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth200 tdoverflowmax200', 'autofocusoncreate'=>1),

-		'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'help'=>"", 'csslist'=>'maxwidth125 tdoverflowmax50'),

-		'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketCategory', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100 tdoverflowmax200'),

-		'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),

-		'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>'isModEnabled("societe")', 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"OrganizationEventLinkToThirdParty", 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),

-		'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),

-		'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>'$conf->project->enabled', 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"),

-		'fk_contract' => array('type'=>'integer:Contrat:contrat/class/contrat.class.php', 'label'=>'Contract', 'visible'=>-1, 'enabled'=>'$conf->contract->enabled', 'position'=>53, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToContract"),

-		//'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""),	// what is this ?

-		'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1, 'csslist'=>'nowraponall'),

-		'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>-1, 'enabled'=>1, 'position'=>501, 'notnull'=>1, 'csslist'=>'nowraponall'),

-		'date_last_msg_sent' => array('type'=>'datetime', 'label'=>'TicketLastMessageDate', 'visible'=>0, 'enabled'=>1, 'position'=>502, 'notnull'=>-1),

-		'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),

-		'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),

-		'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),

-		'message' => array('type'=>'html', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),

-		'email_msgid' => array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'help'=>'EmailMsgIDDesc', 'csslist'=>'tdoverflowmax100'),

-		'email_date' => array('type'=>'datetime', 'label'=>'EmailDate', 'visible'=>-2, 'enabled'=>1, 'position'=>541),

-		'progress' => array('type'=>'integer', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>"", 'isameasure'=>2, 'csslist'=>'width50'),

-		'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position'=>550, 'notnull'=>1),

-		'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 2 => 'Assigned', 3 => 'InProgress', 5 => 'NeedMoreInformation', 7 => 'OnHold', 8 => 'SolvedClosed', 9 => 'Deleted')),

-		'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),

-	);

-	// END MODULEBUILDER PROPERTIES

+	const STATUS_NEED_MORE_INFO = 5;

+	const STATUS_WAITING = 7;

+	const STATUS_CLOSED = 8;

+	const STATUS_CANCELED = 9;

@@ -361,2 +227,0 @@
-		global $conf;

-

@@ -365,20 +230,2 @@
-		$this->labelStatusShort = array(

-			self::STATUS_NOT_READ => 'Unread',

-			self::STATUS_READ => 'Read',

-			self::STATUS_ASSIGNED => 'Assigned',

-			self::STATUS_IN_PROGRESS => 'InProgress',

-			self::STATUS_WAITING => 'OnHold',

-			self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',

-			self::STATUS_CLOSED => 'SolvedClosed',

-			self::STATUS_CANCELED => 'Canceled'

-		);

-		$this->labelStatus = array(

-			self::STATUS_NOT_READ => 'Unread',

-			self::STATUS_READ => 'Read',

-			self::STATUS_ASSIGNED => 'Assigned',

-			self::STATUS_IN_PROGRESS => 'InProgress',

-			self::STATUS_WAITING => 'OnHold',

-			self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',

-			self::STATUS_CLOSED => 'SolvedClosed',

-			self::STATUS_CANCELED => 'Canceled'

-		);

+		$this->statuts_short = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Suspended', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled');

+		$this->statuts       = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Suspended', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled');

@@ -434,5 +280,0 @@
-			if (dol_strlen($this->message) > 65000) {

-				$this->errors[] = 'ErrorFieldTooLong';

-				dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);

-				$result = -1;

-			}

@@ -483 +325 @@
-	 *  @return int                      Return integer <0 if KO, Id of created object if OK

+	 *  @return int                      <0 if KO, Id of created object if OK

@@ -487,2 +329 @@
-		global $conf;

-

+		global $conf, $langs;

@@ -493,3 +334 @@
-		if (empty($this->track_id)) {

-			$this->track_id = generate_random_id(16);

-		}

+		if (empty($this->track_id)) $this->track_id = generate_random_id(16);

@@ -502,2 +340,0 @@
-			$this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);

-

@@ -510 +346,0 @@
-			$sql .= "fk_contract,";

@@ -514,2 +349,0 @@
-			$sql .= "email_msgid,";

-			$sql .= "email_date,";

@@ -529,2 +363 @@
-			$sql .= "notify_tiers_at_create,";

-			$sql .= "ip";

+			$sql .= "notify_tiers_at_create";

@@ -536 +368,0 @@
-			$sql .= " ".($this->fk_contract > 0 ? $this->db->escape($this->fk_contract) : "null").",";

@@ -538 +370 @@
-			$sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? $user->id : 'NULL') : ($this->fk_user_create > 0 ? $this->fk_user_create : 'NULL')).",";

+			$sql .= " ".($this->fk_user_create > 0 ? $this->fk_user_create : ($user->id > 0 ? $user->id : 'NULL')).",";

@@ -540,2 +371,0 @@
-			$sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";

-			$sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";

@@ -549 +379 @@
-			$sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";

+			$sql .= " ".(!isset($this->category_code) ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";

@@ -553,2 +383,2 @@
-			$sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");

-			$sql .= ", ".((int) $this->entity);

+			$sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'")."";

+			$sql .= ", ".$conf->entity;

@@ -556 +385,0 @@
-			$sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");

@@ -561 +390 @@
-			dol_syslog(get_class($this)."::create", LOG_DEBUG);

+			dol_syslog(get_class($this)."::create sql=".$sql, LOG_DEBUG);

@@ -570,6 +399,8 @@
-			}

-

-			if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT')) {

-				// add creator as contributor

-				if ($this->add_contact($user->id, 'CONTRIBUTOR', 'internal') < 0) {

-					$error++;

+

+				if (!$notrigger) {

+					// Call trigger

+					$result = $this->call_trigger('TICKET_CREATE', $user);

+					if ($result < 0) {

+						$error++;

+					}

+					// End call triggers

@@ -578,7 +408,0 @@
-

-			if (!$error && $this->fk_user_assign > 0) {

-				if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {

-					$error++;

-				}

-			}

-

@@ -592,9 +415,0 @@
-			}

-

-			if (!$error && !$notrigger) {

-				// Call trigger

-				$result = $this->call_trigger('TICKET_CREATE', $user);

-				if ($result < 0) {

-					$error++;

-				}

-				// End call triggers

@@ -625,7 +440,6 @@
-	 *  @param  int        	$id    			Id object

-	 *  @param	string		$ref			Ref

-	 *  @param	string		$track_id		Track id, a hash like ref

-	 *  @param	string		$email_msgid	Email msgid

-	 *  @return int              			Return integer <0 if KO, >0 if OK

-	 */

-	public function fetch($id = '', $ref = '', $track_id = '', $email_msgid = '')

+	 *  @param  int        	$id    		Id object

+	 *  @param	string		$ref		Ref

+	 *  @param	string		$track_id	Track id, a hash like ref

+	 *  @return int              		<0 if KO, >0 if OK

+	 */

+	public function fetch($id = '', $ref = '', $track_id = '')

@@ -636 +450 @@
-		if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {

+		if (!$id && !$track_id && !$ref) {

@@ -638 +452 @@
-			dol_print_error('', get_class($this)."::fetch ".$this->error);

+			dol_print_error(get_class($this)."::fetch ".$this->error);

@@ -644 +457,0 @@
-		$sql .= " t.entity,";

@@ -649 +461,0 @@
-		$sql .= " t.fk_contract,";

@@ -653,2 +464,0 @@
-		$sql .= " t.email_msgid,";

-		$sql .= " t.email_date,";

@@ -666 +475,0 @@
-		$sql .= " t.date_last_msg_sent,";

@@ -668,3 +477,2 @@
-		$sql .= " t.tms,";

-		$sql .= " t.ip,";

-		$sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";

+		$sql .= " t.tms";

+		$sql .= ", type.code as type_code, type.label as type_label, category.code as category_code, category.label as category_label, severity.code as severity_code, severity.label as severity_label";

@@ -677 +485 @@
-			$sql .= " WHERE t.rowid = ".((int) $id);

+			$sql .= " WHERE t.rowid = ".$this->db->escape($id);

@@ -680 +488,3 @@
-			if (!empty($ref)) {

+			if ($track_id) {

+				$sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";

+			} elseif ($ref) {

@@ -682,8 +492,4 @@
-			} elseif ($track_id) {

-				$sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";

-			} else {

-				$sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";

-			}

-		}

-

-		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);

+			}

+		}

+

+		dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);

@@ -692 +498,2 @@
-			if ($this->db->num_rows($resql)) {

+			if ($this->db->num_rows($resql))

+			{

@@ -696 +502,0 @@
-				$this->entity = $obj->entity;

@@ -702 +507,0 @@
-				$this->fk_contract = $obj->fk_contract;

@@ -706,2 +510,0 @@
-				$this->email_msgid = $obj->email_msgid;

-				$this->email_date = $this->db->jdate($obj->email_date);

@@ -710 +512,0 @@
-				$this->ip = $obj->ip;

@@ -713 +515 @@
-				$this->fk_statut = $this->status; // For backward compatibility

+				$this->fk_statut = $this->status;		// For backward compatibility

@@ -720 +522,2 @@
-				$label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));

+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));

@@ -724 +527,2 @@
-				$label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));

+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));

@@ -728 +532,2 @@
-				$label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));

+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));

@@ -735 +539,0 @@
-				$this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);

@@ -744 +548,3 @@
-			} else {

+			}

+			else

+			{

@@ -757,8 +563,9 @@
-	 * @param  User   $user      	User for action

-	 * @param  string $sortorder 	Sort order

-	 * @param  string $sortfield 	Sort field

-	 * @param  int    $limit     	page number

-	 * @param  int    $offset    	Offset for query

-	 * @param  int    $arch      	archive or not (not used)

-	 * @param  array  $filter    	Filter for query

-	 * @return int 					Return integer <0 if KO, >0 if OK

+	 * @param  User   $user      User for action

+	 * @param  string $sortorder Sort order

+	 * @param  string $sortfield Sort field

+	 * @param  int    $limit     page number

+	 * @param  int    $offset    Offset for query

+	 * @param  int    $arch      archive or not (not used)

+	 * @param  array  $filter    Filter for query

+	 *                           output

+	 * @return int <0 if KO, >0 if OK

@@ -768 +575,3 @@
-		global $langs, $extrafields;

+		global $langs;

+

+		$extrafields = new ExtraFields($this->db);

@@ -779 +587,0 @@
-		$sql .= " t.fk_contract,";

@@ -785 +593 @@
-		$sql .= " t.fk_statut as status,";

+		$sql .= " t.fk_statut,";

@@ -794 +601,0 @@
-		$sql .= " t.date_last_msg_sent,";

@@ -799,4 +606,2 @@
-		if ($extrafields->attributes[$this->table_element]['count']> 0) {

-			foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {

-				$sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');

-			}

+		foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {

+			$sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key.' as options_'.$key : '');

@@ -811,6 +616,4 @@
-		if ($extrafields->attributes[$this->table_element]['count']> 0) {

-			if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {

-				$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";

-			}

-		}

-		if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {

+		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {

+			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";

+		}

+		if (!$user->rights->societe->client->voir && !$user->socid) {

@@ -826 +629 @@
-					$sql .= " AND ".$key." = '".$this->db->escape($value)."'";

+					$sql .= ' AND '.$key.' = \''.$value.'\'';

@@ -831 +634 @@
-						$sql .= " AND ".$key." IN (".$this->db->sanitize(implode(',', $value)).")";

+						$sql .= 'AND '.$key.' IN ('.implode(',', $value).')';

@@ -833 +636 @@
-						$sql .= " AND ".$key.' = '.((int) $value);

+						$sql .= ' AND '.$key.' = '.$this->db->escape($value);

@@ -835,2 +637,0 @@
-				} elseif ($key == 't.fk_contract') {

-					$sql .= " AND ".$key.' = '.((int) $value);

@@ -838 +639 @@
-					$sql .= " AND ".$key." LIKE '%".$this->db->escape($value)."%'";

+					$sql .= ' AND '.$key.' LIKE \'%'.$value.'%\'';

@@ -842,2 +643,2 @@
-		if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {

-			$sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);

+		if (!$user->rights->societe->client->voir && !$user->socid) {

+			$sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".$user->id;

@@ -845,4 +646,4 @@
-			$sql .= " AND t.fk_soc = ".((int) $user->socid);

-		}

-

-		$sql .= $this->db->order($sortfield, $sortorder);

+			$sql .= " AND t.fk_soc = ".$user->socid;

+		}

+

+		$sql .= " ORDER BY ".$sortfield.' '.$sortorder;

@@ -850,4 +651,4 @@
-			$sql .= $this->db->plimit($limit + 1, $offset);

-		}

-

-		dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);

+			$sql .= ' '.$this->db->plimit($limit + 1, $offset);

+		}

+

+		dol_syslog(get_class($this)."::fetch_all sql=".$sql, LOG_DEBUG);

@@ -866 +667 @@
-					$line = new self($this->db);

+					$line = new TicketsLine();

@@ -869 +670 @@
-					//$line->rowid = $obj->rowid;

+					$line->rowid = $obj->rowid;

@@ -874 +674,0 @@
-					$line->fk_contract = $obj->fk_contract;

@@ -877,0 +678,3 @@
+					$line->user_create_lastname = $obj->user_create_lastname;

+					$line->user_create_firstname = $obj->user_create_firstname;

+

@@ -878,0 +682,2 @@
+					$line->user_assign_lastname = $obj->user_assign_lastname;

+					$line->user_assign_firstname = $obj->user_assign_firstname;

@@ -882,2 +687 @@
-					$line->fk_statut = $obj->status;

-					$line->status = $obj->status;

+					$line->fk_statut = $obj->fk_statut;

@@ -888 +692,2 @@
-					$label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));

+					// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+					$label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));

@@ -892 +697,2 @@
-					$label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));

+					// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+					$label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));

@@ -896 +702,2 @@
-					$label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));

+					// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+					$label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));

@@ -901 +707,0 @@
-					$line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);

@@ -905,6 +711,4 @@
-					if ($extrafields->attributes[$this->table_element]['count']> 0) {

-						if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {

-							foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {

-								$tmpkey = 'options_'.$key;

-								$line->{$tmpkey} = $obj->$tmpkey;

-							}

+					if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {

+						foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {

+							$tmpkey = 'options_'.$key;

+							$line->{$tmpkey} = $obj->$tmpkey;

@@ -912,0 +717 @@
+

@@ -921 +726 @@
-			dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);

+			dol_syslog(get_class($this)."::fetch_all ".$this->error, LOG_ERR);

@@ -931,4 +736,5 @@
-	 *  @return int                     Return integer <0 if KO, >0 if OK

-	 */

-	public function update($user, $notrigger = 0)

-	{

+	 *  @return int                     <0 if KO, >0 if OK

+	 */

+	public function update($user = 0, $notrigger = 0)

+	{

+		global $conf, $langs, $hookmanager;

@@ -936,5 +741,0 @@
-

-		// $this->oldcopy should have been set by the caller of update (here properties were already modified)

-		//if (empty($this->oldcopy)) {

-		//	$this->oldcopy = dol_clone($this);

-		//}

@@ -959,4 +759,0 @@
-		if (isset($this->fk_contract)) {

-			$this->fk_contract = (int) $this->fk_contract;

-		}

-

@@ -981,5 +777,0 @@
-			if (dol_strlen($this->message) > 65000) {

-				$this->errors[] = 'ErrorFieldTooLong';

-				dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);

-				return -1;

-			}

@@ -1024 +815,0 @@
-		$sql .= " fk_contract=".(isset($this->fk_contract) ? "'".$this->db->escape($this->fk_contract)."'" : "null").",";

@@ -1030 +821 @@
-		$sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "0").",";

+		$sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "null").",";

@@ -1039,3 +830,2 @@
-		$sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";

-		$sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');

-		$sql .= " WHERE rowid=".((int) $this->id);

+		$sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null')."";

+		$sql .= " WHERE rowid=".$this->id;

@@ -1065 +855 @@
-			// End call triggers

+			  // End call triggers

@@ -1087 +877 @@
-	 *  @return int                     Return integer <0 if KO, >0 if OK

+	 *  @return int                     <0 if KO, >0 if OK

@@ -1119,3 +909 @@
-			if ($res < 0) {

-				$error++;

-			}

+			if ($res < 0) $error++;

@@ -1133,13 +920,0 @@
-		// Delete all child tables

-

-		if (!$error) {

-			$sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";

-			$sql .= " WHERE fk_ticket = ".(int) $this->id;

-

-			$result = $this->db->query($sql);

-			if (!$result) {

-				$error++;

-				$this->errors[] = $this->db->lasterror();

-			}

-		}

-

@@ -1148 +923 @@
-			$sql .= " WHERE rowid=".((int) $this->id);

+			$sql .= " WHERE rowid=".$this->id;

@@ -1223 +998 @@
-	 *     @return int

+	 *     @return void

@@ -1228 +1003 @@
-		$this->entity = 1;

+

@@ -1237 +1012 @@
-		$this->status = 0;

+		$this->fk_statut = 0;

@@ -1240 +1015 @@
-		//$this->timing = '30';

+		$this->timing = '30';

@@ -1246 +1020,0 @@
-		$this->date_last_msg_sent = '';

@@ -1249,8 +1023,7 @@
-		return 1;

-	}

-

-	/**

-	 * Print selected status

-	 *

-	 * @param 	string    $selected   	Selected status

-	 * @return 	void

+	}

+

+	/**

+	 * print selected status

+	 *

+	 * @param string    $selected   selected status

+	 * @return void

@@ -1260,8 +1033,8 @@
-		print Form::selectarray('search_fk_statut', $this->labelStatusShort, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');

-	}

-

-

-	/**

-	 * Load into a cache the types of tickets (setup done into dictionaries)

-	 *

-	 * @return 	int       Number of lines loaded, 0 if already loaded, <0 if KO

+		print Form::selectarray('search_fk_statut', $this->statuts_short, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');

+	}

+

+

+	/**

+	 *      Charge dans cache la liste des types de tickets (paramétrable dans dictionnaire)

+	 *

+	 *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO

@@ -1280,2 +1053 @@
-		$sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";

-		$sql .= " AND active > 0";

+		$sql .= " WHERE active > 0";

@@ -1283 +1055 @@
-		dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);

+		dol_syslog(get_class($this)."::load_cache_type_tickets sql=".$sql, LOG_DEBUG);

@@ -1290 +1062,2 @@
-				$label = ($langs->trans("TicketTypeShort".$obj->code) != "TicketTypeShort".$obj->code ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label = ($langs->trans("TicketTypeShort".$obj->code) != ("TicketTypeShort".$obj->code) ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

@@ -1305,11 +1078,9 @@
-	 *      Load into a cache array, the list of ticket categories (setup done into dictionary)

-	 *

-	 *      @param	int		$publicgroup	0=No public group, 1=Public group only, -1=All

-	 *      @return int             		Number of lines loaded, 0 if already loaded, <0 if KO

-	 */

-	public function loadCacheCategoriesTickets($publicgroup = -1)

-	{

-		global $conf, $langs;

-

-		if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {

-			// Cache already loaded

+	 *      Charge dans cache la liste des catégories de tickets (paramétrable dans dictionnaire)

+	 *

+	 *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO

+	 */

+	public function loadCacheCategoriesTickets()

+	{

+		global $langs;

+

+		if (!empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {

@@ -1318,2 +1089,3 @@
-

-		$sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";

+		// Cache deja charge

+

+		$sql = "SELECT rowid, code, label, use_default, pos, description";

@@ -1321,5 +1093 @@
-		$sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";

-		$sql .= " AND active > 0";

-		if ($publicgroup > -1) {

-			$sql .= " AND public = ".((int) $publicgroup);

-		}

+		$sql .= " WHERE active > 0";

@@ -1327,3 +1095 @@
-

-		dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);

-

+		dol_syslog(get_class($this)."::load_cache_categories_tickets sql=".$sql, LOG_DEBUG);

@@ -1336,0 +1103,3 @@
+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label = ($langs->trans("TicketCategoryShort".$obj->code) != ("TicketCategoryShort".$obj->code) ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

+				$this->cache_category_tickets[$obj->rowid]['label'] = $label;

@@ -1339,10 +1107,0 @@
-				$this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;

-				$this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;

-				$this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;

-				$this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;

-

-				// If  translation exists, we use it to store already translated string.

-				// Warning: You should not use this and recompute the translated string into caller code to get the value into expected language

-				$label = ($langs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

-				$this->cache_category_tickets[$obj->rowid]['label'] = $label;

-

@@ -1374,2 +1133 @@
-		$sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";

-		$sql .= " AND active > 0";

+		$sql .= " WHERE active > 0";

@@ -1377 +1135 @@
-		dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);

+		dol_syslog(get_class($this)."::loadCacheSeveritiesTickets sql=".$sql, LOG_DEBUG);

@@ -1386 +1144,2 @@
-				$label = ($langs->trans("TicketSeverityShort".$obj->code) != "TicketSeverityShort".$obj->code ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

+				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut

+				$label = ($langs->trans("TicketSeverityShort".$obj->code) != ("TicketSeverityShort".$obj->code) ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));

@@ -1403,2 +1162,2 @@
-	 * @param      	int		$mode     	0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto

-	 * @return     	string    			Label

+	 * @param      int		$mode     0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto

+	 * @return     string    			  Label

@@ -1408 +1167 @@
-		return $this->LibStatut($this->fk_statut, $mode, 0, $this->progress);

+		return $this->libStatut($this->fk_statut, $mode);

@@ -1414,9 +1173,7 @@
-	 * Return status label of object

-	 *

-	 * @param	string		$status			Id status

-	 * @param	int			$mode			0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto

-	 * @param	int			$notooltip		1=No tooltip

-	 * @param	int			$progress		Progression (0 to 100)

-	 * @return	string						Label

-	 */

-	public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)

+	 *    Return status label of object

+	 *

+	 *    @param      string 	$status      Id status

+	 *    @param      int		$mode        0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto

+	 *    @return     string     			 Label

+	 */

+	public function LibStatut($status, $mode = 0)

@@ -1425,74 +1181,0 @@
-		global $langs, $hookmanager;

-

-		$labelStatus = (isset($status) && !empty($this->labelStatus[$status])) ? $this->labelStatus[$status] : '';

-		$labelStatusShort = (isset($status) && !empty($this->labelStatusShort[$status]))? $this->labelStatusShort[$status] : '';

-

-		switch ($status) {

-			case self::STATUS_NOT_READ:

-				$statusType = 'status0';

-				break;

-			case self::STATUS_READ:

-				$statusType = 'status1';

-				break;

-			case self::STATUS_ASSIGNED:

-				$statusType = 'status2';

-				break;

-			case self::STATUS_IN_PROGRESS:

-				$statusType = 'status4';

-				break;

-			case self::STATUS_WAITING:

-				$statusType = 'status7';

-				break;

-			case self::STATUS_NEED_MORE_INFO:

-				$statusType = 'status3';

-				break;

-			case self::STATUS_CANCELED:

-				$statusType = 'status9';

-				break;

-			case self::STATUS_CLOSED:

-				$statusType = 'status6';

-				break;

-			default:

-				$labelStatus = 'Unknown';

-				$labelStatusShort = 'Unknown';

-				$statusType = 'status0';

-				$mode = 0;

-		}

-

-		$parameters = array(

-			'status'          => $status,

-			'mode'            => $mode,

-		);

-

-		// Note that $action and $object may have been modified by hook

-		$reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);

-

-		if ($reshook > 0) {

-			return $hookmanager->resPrint;

-		}

-

-		$params = array();

-		if ($notooltip) {

-			$params = array('tooltip' => 'no');

-		}

-

-		$labelStatus = $langs->transnoentitiesnoconv($labelStatus);

-		$labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);

-

-		if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {

-			$labelStatus .= ' ('.round($progress).'%)';

-			$labelStatusShort .= ' ('.round($progress).'%)';

-		}

-

-		return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);

-	}

-

-	/**

-	 * getTooltipContentArray

-	 *

-	 * @param array $params ex option, infologin

-	 * @since v18

-	 * @return array

-	 */

-	public function getTooltipContentArray($params)

-	{

@@ -1501,24 +1184,37 @@
-		$langs->load('ticket');

-		$nofetch = !empty($params['nofetch']);

-

-		$datas = array();

-		$datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';

-		$datas['picto'] .= ' '.$this->getLibStatut(4);

-		$datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;

-		$datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;

-		$datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;

-		if ($this->date_creation) {

-			$datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');

-		}

-		if ($this->date_modification) {

-			$datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');

-		}

-		// show categories for this record only in ajax to not overload lists

-		if (isModEnabled('categorie') && !$nofetch) {

-			require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';

-			$form = new Form($this->db);

-			$datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);

-		}

-

-		return $datas;

-	}

+		$labelStatus = $this->statuts[$status];

+		$labelStatusShort = $this->statuts_short[$status];

+

+		if ($status == self::STATUS_NOT_READ) {

+			$statusType = 'status0';

+		}

+		elseif ($status == self::STATUS_READ) {

+			$statusType = 'status1';

+		}

+		elseif ($status == self::STATUS_ASSIGNED) {

+			$statusType = 'status3';

+		}

+		elseif ($status == self::STATUS_IN_PROGRESS) {

+			$statusType = 'status4';

+		}

+		elseif ($status == self::STATUS_WAITING) {

+			$statusType = 'status3';

+		}

+		elseif ($status == self::STATUS_NEED_MORE_INFO) {

+			$statusType = 'status9';

+		}

+		elseif ($status == self::STATUS_CANCELED) {

+			$statusType = 'status9';

+		}

+		elseif ($status == self::STATUS_CLOSED) {

+			$statusType = 'status6';

+		}

+		else {

+			$labelStatus = $langs->trans('Unknown');

+			$labelStatusShort = $langs->trans('Unknown');

+			$statusType = 'status0';

+			$mode = 0;

+		}

+

+		return dolGetStatus($langs->trans($labelStatus), $langs->trans($labelStatusShort), '', $statusType, $mode);

+	}

+

@@ -1538,5 +1234,5 @@
-		global $action, $conf, $hookmanager, $langs;

-

-		if (!empty($conf->dol_no_mouse_hover)) {

-			$notooltip = 1; // Force disable tooltips

-		}

+		global $db, $conf, $langs;

+		global $dolibarr_main_authentication, $dolibarr_main_demo;

+		global $menumanager;

+

+		if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips

@@ -1545,20 +1241,12 @@
-

-		$params = [

-			'id' => $this->id,

-			'objecttype' => $this->element,

-			'option' => $option,

-			'nofetch' => 1,

-		];

-		$classfortooltip = 'classfortooltip';

-		$dataparams = '';

-		if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {

-			$classfortooltip = 'classforajaxtooltip';

-			$dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';

-			$label = '';

-		} else {

-			$label = implode($this->getTooltipContentArray($params));

-		}

-

-		$url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;

-

-		if ($option != 'nolink') {

+		$companylink = '';

+

+		$label = '<u>'.$langs->trans("Ticket").'</u>';

+		$label .= '<br>';

+		$label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref.'<br>';

+		$label .= '<b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id.'<br>';

+		$label .= '<b>'.$langs->trans('Subject').':</b> '.$this->subject;

+

+		$url = dol_buildpath('/ticket/card.php', 1).'?id='.$this->id;

+

+		if ($option != 'nolink')

+		{

@@ -1567,6 +1255,2 @@
-			if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {

-				$add_save_lastsearch_values = 1;

-			}

-			if ($add_save_lastsearch_values) {

-				$url .= '&save_lastsearch_values=1';

-			}

+			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;

+			if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';

@@ -1576,2 +1260,4 @@
-		if (empty($notooltip)) {

-			if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {

+		if (empty($notooltip))

+		{

+			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))

+			{

@@ -1581,5 +1267,4 @@
-			$linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');

-			$linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';

-		} else {

-			$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');

-		}

+			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';

+			$linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';

+		}

+		else $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');

@@ -1592,6 +1277,2 @@
-		if ($withpicto) {

-			$result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);

-		}

-		if ($withpicto != 2) {

-			$result .= $this->ref;

-		}

+		if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);

+		if ($withpicto != 2) $result .= $this->ref;

@@ -1601,9 +1281,0 @@
-		$hookmanager->initHooks(array('ticketdao'));

-		$parameters = array('id' => $this->id, 'getnomurl' => &$result);

-		$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks

-		if ($reshook > 0) {

-			$result = $hookmanager->resPrint;

-		} else {

-			$result .= $hookmanager->resPrint;

-		}

-

@@ -1617,3 +1289,3 @@
-	 *    @param    User		$user			Object user

-	 *    @param	int			$notrigger		No trigger

-	 *    @return   int							Return integer <0 if KO, 0=nothing done, >0 if OK

+	 *    @param    User		$user			    Object user

+	 *    @param	  int			$notrigger		No trigger

+	 *    @return   int							      <0 if KO, >0 if OK

@@ -1623 +1295 @@
-		global $langs;

+		global $conf, $langs;

@@ -1628,2 +1299,0 @@
-			$this->oldcopy = dol_clone($this);

-

@@ -1633,2 +1303,2 @@
-			$sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";

-			$sql .= " WHERE rowid = ".((int) $this->id);

+			$sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read='".$this->db->idate(dol_now())."'";

+			$sql .= " WHERE rowid = ".$this->id;

@@ -1639,2 +1309,2 @@
-				$this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));

-				$this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));

+				$this->actionmsg = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));

+				$this->actionmsg2 = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));

@@ -1667,6 +1337,4 @@
-

-		return 0;

-	}

-

-	/**

-	 *    Set an assigned user to a ticket.

+	}

+

+	/**

+	 *    Mark a message as read

@@ -1677 +1345 @@
-	 *    @return   int							Return integer <0 if KO, 0=Nothing done, >0 if OK

+	 *    @return   int							<0 if KO, 0=Nothing done, >0 if OK

@@ -1680,0 +1349,2 @@
+		global $conf, $langs;

+

@@ -1681,0 +1352 @@
+		$this->db->begin();

@@ -1685,2 +1355,0 @@
-		$this->db->begin();

-

@@ -1688,3 +1357,6 @@
-		if ($id_assign_user > 0) {

-			$sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;

-		} else {

+		if ($id_assign_user > 0)

+		{

+			$sql .= " SET fk_user_assign=".$id_assign_user.", fk_statut = ".Ticket::STATUS_ASSIGNED;

+		}

+		else

+		{

@@ -1693 +1365 @@
-		$sql .= " WHERE rowid = ".((int) $this->id);

+		$sql .= " WHERE rowid = ".$this->id;

@@ -1697 +1369,2 @@
-		if ($resql) {

+		if ($resql)

+		{

@@ -1725,0 +1399,136 @@
+

+	/**

+	 *  Send notification of changes by email

+	 *

+	 * 	@param  User   $user    		User that create

+	 * 	@param  string $message 		Log message

+	 * 	@return int                 	<0 if KO, >0 if OK (number of emails sent)

+	 */

+	private function sendLogByEmail($user, $message)

+	{

+		global $conf, $langs;

+

+		$nb_sent = 0;

+

+		$langs->load('ticket');

+

+		// Retrieve email of all contacts (internal and external)

+		$contacts = $this->listeContact(-1, 'internal');

+		$contacts = array_merge($contacts, $this->listeContact(-1, 'external'));

+

+		/* If origin_email and no socid, we add email to the list * */

+		if (!empty($this->origin_email) && empty($this->fk_soc)) {

+			$array_ext = array(array('firstname' => '', 'lastname' => '', 'email' => $this->origin_email, 'libelle' => $langs->transnoentities('TicketEmailOriginIssuer'), 'socid' => "-1"));

+			$contacts = array_merge($contacts, $array_ext);

+		}

+

+		if (!empty($this->fk_soc)) {

+			$this->fetch_thirdparty($this->fk_soc);

+			$array_company = array(array('firstname' => '', 'lastname' => $this->client->name, 'email' => $this->client->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $this->client->id));

+			$contacts = array_merge($contacts, $array_company);

+		}

+

+		// foreach contact send email with notification message

+		if (count($contacts) > 0) {

+			foreach ($contacts as $key => $info_sendto) {

+				$message = '';

+				$subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities('TicketNotificationEmailSubject', $this->track_id);

+				$message .= $langs->transnoentities('TicketNotificationEmailBody', $this->track_id)."\n\n";

+				$message .= $langs->transnoentities('Title').' : '.$this->subject."\n";

+

+				$recipient_name = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1');

+				$recipient = (!empty($recipient_name) ? $recipient_name : $info_sendto['email']).' ('.strtolower($info_sendto['libelle']).')';

+				$message .= $langs->transnoentities('TicketNotificationRecipient').' : '.$recipient."\n";

+				$message .= "\n";

+				$message .= '* '.$langs->transnoentities('TicketNotificationLogMessage').' *'."\n";

+				$message .= dol_html_entity_decode($log_message, ENT_QUOTES)."\n";

+

+				if ($info_sendto['source'] == 'internal') {

+					$url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$this->track_id;

+					$message .= "\n".$langs->transnoentities('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$this->track_id.'</a>'."\n";

+				} else {

+					$url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/' : dol_buildpath('/public/ticket/view.php', 2)).'?track_id='.$this->track_id;

+					$message .= "\n".$langs->transnoentities('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$this->track_id.'</a>'."\n";

+				}

+

+				$message .= "\n";

+				$message .= $langs->transnoentities('TicketEmailPleaseDoNotReplyToThisEmail')."\n";

+

+				$from = $conf->global->MAIN_INFO_SOCIETE_NOM.'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>';

+				$replyto = $from;

+

+				// Init to avoid errors

+				$filepath = array();

+				$filename = array();

+				$mimetype = array();

+

+				$message = dol_nl2br($message);

+

+				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, $info_sendto['email'], $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, 0);

+				if ($mailfile->error || $mailfile->errors) {

+					setEventMessages($mailfile->error, $mailfile->errors, 'errors');

+				} else {

+					$result = $mailfile->sendfile();

+					if ($result > 0) {

+						$nb_sent++;

+					}

+				}

+				if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {

+					$conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;

+				}

+			}

+

+			setEventMessages($langs->trans('TicketNotificationNumberEmailSent', $nb_sent), null, 'mesgs');

+		}

+

+		return $nb_sent;

+	}

+

+	/**

+	 *      Charge la liste des actions sur le ticket

+	 *

+	 *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO

+	 */

+	public function loadCacheLogsTicket()

+	{

+		global $langs;

+

+		if (is_array($this->cache_logs_ticket) && count($this->cache_logs_ticket)) {

+			return 0;

+		}

+		// Cache deja charge

+

+		// TODO Read the table llx_actioncomm

+		/*

+        $sql = "SELECT rowid, fk_user_create, datec, message";

+        $sql .= " FROM " . MAIN_DB_PREFIX . "ticket_logs";

+        $sql .= " WHERE fk_track_id ='" . $this->db->escape($this->track_id) . "'";

+        $sql .= " ORDER BY datec DESC";

+

+        $resql = $this->db->query($sql);

+        if ($resql) {

+            $num = $this->db->num_rows($resql);

+            $i = 0;

+            while ($i < $num) {

+                $obj = $this->db->fetch_object($resql);

+                $this->cache_logs_ticket[$i]['id'] = $obj->rowid;

+                $this->cache_logs_ticket[$i]['fk_user_create'] = $obj->fk_user_create;

+                $this->cache_logs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);

+                $this->cache_logs_ticket[$i]['message'] = $obj->message;

+                $i++;

+            }

+            return $num;

+        } else {

+            $this->error = "Error " . $this->db->lasterror();

+            dol_syslog(get_class($this) . "::loadCacheLogsTicket " . $this->error, LOG_ERR);

+            return -1;

+        }*/

+

+		return 0;

+	}

+

@@ -1729,10 +1538,8 @@
-	 * @param 	User 	$user      		  	User that creates

-	 * @param 	int  	$notrigger 		  	0=launch triggers after, 1=disable triggers

-	 * @param 	array	$filename_list      List of files to attach (full path of filename on file system)

-	 * @param 	array	$mimetype_list      List of MIME type of attached files

-	 * @param 	array	$mimefilename_list  List of attached file name in message

-	 * @param 	boolean	$send_email      	Whether the message is sent by email

-	 * @param   int     $public_area    	0=Default, 1 if we are creating the message from a public area (so we can search contact from email to add it as contact of ticket if TICKET_ASSIGN_CONTACT_TO_MESSAGE is set)

-	 * @return 	int						  	Return integer <0 if KO, >0 if OK

-	 */

-	public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false, $public_area = 0)

+	 * @param User 	 $user      		  User that creates

+	 * @param int  	 $notrigger 		  0=launch triggers after, 1=disable triggers

+	 * @param array	 $filename_list       List of files to attach (full path of filename on file system)

+	 * @param array	 $mimetype_list       List of MIME type of attached files

+	 * @param array	 $mimefilename_list   List of attached file name in message

+	 * @return int						  <0 if KO, >0 if OK

+	 */

+	public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())

@@ -1759 +1566 @@
-		$actioncomm->type_code = 'AC_OTH_AUTO';	// This is not an entry that must appears into manual calendar but only into CRM calendar

+		$actioncomm->type_code = 'AC_OTH';

@@ -1763,6 +1569,0 @@
-		}

-		if ($send_email) {

-			$actioncomm->code .= '_SENTBYMAIL';

-		}

-		if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {

-			$actioncomm->email_from = $_SESSION['email_customer'];

@@ -1776 +1577 @@
-		$actioncomm->percentage = -1; // percentage is not relevant for punctual events

+		$actioncomm->percentage = 100;

@@ -1779,15 +1579,0 @@
-		$actioncomm->fk_project = $this->fk_project;

-

-		// add contact id from author email on public interface

-		if ($public_area && !empty($this->origin_email) && getDolGlobalString('TICKET_ASSIGN_CONTACT_TO_MESSAGE')) {

-			$contacts = $this->searchContactByEmail($this->origin_email);

-			if (!empty($contacts)) {

-				// Ensure that contact is active and select first active contact

-				foreach ($contacts as $contact) {

-					if ((int) $contact->statut == 1) {

-						$actioncomm->contact_id = $contact->id;

-						break;

-					}

-				}

-			}

-		}

@@ -1803 +1589,2 @@
-		if (!empty($mimefilename_list) && is_array($mimefilename_list)) {

+		if (!empty($mimefilename_list) && is_array($mimefilename_list))

+		{

@@ -1808 +1595,2 @@
-		if ($actionid <= 0) {

+		if ($actionid <= 0)

+		{

@@ -1831 +1619 @@
-		if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {

+		if (is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {

@@ -1837 +1625 @@
-		$sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";

+		$sql = "SELECT id as rowid, fk_user_author, datec, label, note as message, code";

@@ -1841,3 +1629,3 @@
-		$sql .= " ORDER BY datep DESC";

-

-		dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);

+		$sql .= " ORDER BY datec DESC";

+

+		dol_syslog(get_class($this)."::load_cache_actions_ticket sql=".$sql, LOG_DEBUG);

@@ -1852,3 +1639,0 @@
-				if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {

-					$this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;

-				}

@@ -1856 +1640,0 @@
-				$this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);

@@ -1859 +1643 @@
-				$this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);

+				$this->cache_msgs_ticket[$i]['private'] = ($obj->code == 'TICKET_MSG_PRIVATE' ? 1 : 0);

@@ -1873,9 +1657,8 @@
-	 *    @param    User    $user      	User that close

-	 *    @param	int		$mode		0=Close solved, 1=Close abandonned

-	 *    @return   int		           	Return integer <0 if KO, 0=nothing done, >0 if OK

-	 */

-	public function close(User $user, $mode = 0)

-	{

-		global $conf;

-

-		if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed

+	 *    @param    User    $user      User that close

+	 *    @return   int		           <0 if KO, >0 if OK

+	 */

+	public function close(User $user)

+	{

+		global $conf, $langs;

+

+		if ($this->fk_statut != Ticket::STATUS_CLOSED) { // not closed

@@ -1885,4 +1668,4 @@
-			$sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";

-			$sql .= " WHERE rowid = ".((int) $this->id);

-

-			dol_syslog(get_class($this)."::close mode=".$mode);

+			$sql .= " SET fk_statut=".Ticket::STATUS_CLOSED.", progress=100, date_close='".$this->db->idate(dol_now())."'";

+			$sql .= " WHERE rowid = ".$this->id;

+

+			dol_syslog(get_class($this)."::close sql=".$sql);

@@ -1894,13 +1677,11 @@
-				if (isModEnabled('ficheinter') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {

-					dol_syslog("We have closed the ticket, so we close all linked interventions");

-					$this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');

-					if ($this->linkedObjectsIds) {

-						foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {

-							$fichinter = new Fichinter($this->db);

-							$fichinter->fetch($fichinter_id);

-							if ($fichinter->statut == 0) {

-								$result = $fichinter->setValid($user);

-								if (!$result) {

-									$this->errors[] = $fichinter->error;

-									$error++;

-								}

+				$this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');

+				if ($this->linkedObjectsIds)

+				{

+					foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {

+						$fichinter = new Fichinter($this->db);

+						$fichinter->fetch($fichinter_id);

+						if ($fichinter->statut == 0) {

+							$result = $fichinter->setValid($user);

+							if (!$result) {

+								$this->errors[] = $fichinter->error;

+								$error++;

@@ -1908,6 +1689,6 @@
-							if ($fichinter->statut < 3) {

-								$result = $fichinter->setStatut(3);

-								if (!$result) {

-									$this->errors[] = $fichinter->error;

-									$error++;

-								}

+						}

+						if ($fichinter->statut < 3) {

+							$result = $fichinter->setStatut(3);

+							if (!$result) {

+								$this->errors[] = $fichinter->error;

+								$error++;

@@ -1942,2 +1722,0 @@
-

-		return 0;

@@ -1949,5 +1728,5 @@
-	 *     @param  string 		$email   		Email

-	 *     @param  int    		$type    		Type of thirdparties (0=any, 1=customer, 2=prospect, 3=supplier)

-	 *     @param  array  		$filters 		Array of couple field name/value to filter the companies with the same name

-	 *     @param  string 		$clause  		Clause for filters

-	 *     @return array|int    		   		Array of thirdparties object

+	 *     @param  string $email   		Email

+	 *     @param  int    $type    		Type of thirdparties (0=any, 1=customer, 2=prospect, 3=supplier)

+	 *     @param  array  $filters 		Array of couple field name/value to filter the companies with the same name

+	 *     @param  string $clause  		Clause for filters

+	 *     @return array        		Array of thirdparties object

@@ -1958 +1736,0 @@
-		$exact = 0;

@@ -1965 +1743 @@
-				$sql .= " AND client = ".((int) $type);

+				$sql .= " AND client = ".$type;

@@ -1971,2 +1749 @@
-			if (empty($exact)) {

-				$regs = array();

+			if (!$exact) {

@@ -1984 +1761,5 @@
-			$sql .= "email LIKE '".$this->db->escape($email)."'";

+			if (!$case) {

+				$sql .= "email LIKE '".$this->db->escape($email)."'";

+			} else {

+				$sql .= "email LIKE BINARY '".$this->db->escape($email)."'";

+			}

@@ -1988 +1769 @@
-				$sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";

+				$sql .= " ".$clause." ".$field." LIKE BINARY '".$this->db->escape($value)."'";

@@ -2014,4 +1795,4 @@
-	 *     @param  string 		$email 		Email

-	 *     @param  array  		$socid 		Limit to a thirdparty

-	 *     @param  string 		$case  		Respect case

-	 *     @return array|int        		Array of contacts object

+	 *     @param  string $email 	Email

+	 *     @param  array  $socid 	Limit to a thirdparty

+	 *     @param  string $case  	Respect case

+	 *     @return array        	Array of contacts object

@@ -2023 +1804 @@
-		// Forge the search SQL

+		// Generation requete recherche

@@ -2025 +1806 @@
-		$sql .= " WHERE entity IN (".getEntity('contact').")";

+		$sql .= " WHERE entity IN (".getEntity('socpeople').")";

@@ -2027,2 +1808,3 @@
-			$sql .= " AND fk_soc = ".((int) $socid);

-		}

+			$sql .= " AND fk_soc='".$this->db->escape($socid)."'";

+		}

+

@@ -2030,0 +1813 @@
+

@@ -2032 +1815 @@
-				$sql .= "email = '".$this->db->escape($email)."'";

+				$sql .= "email LIKE '".$this->db->escape($email)."'";

@@ -2034 +1817 @@
-				$sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";

+				$sql .= "email LIKE BINARY '".$this->db->escape($email)."'";

@@ -2040 +1823 @@
-			while ($rec = $this->db->fetch_object($res)) {

+			while ($rec = $this->db->fetch_array($res)) {

@@ -2043 +1826 @@
-				$contactstatic->fetch($rec->rowid);

+				$contactstatic->fetch($rec['rowid']);

@@ -2058,2 +1841,2 @@
-	 *    @param  int $id		Id of thirdparty to set or '' to remove

-	 *    @return int           Return integer <0 if KO, >0 if OK

+	 *    @param  int $id Id of thirdparty to set or '' to remove

+	 *    @return int             <0 if KO, >0 if OK

@@ -2066 +1849 @@
-			$sql .= " WHERE rowid = ".((int) $this->id);

+			$sql .= " WHERE rowid = ".$this->id;

@@ -2083 +1866 @@
-	 *    @return int             Return integer <0 if KO, >0 if OK

+	 *    @return int             <0 if KO, >0 if OK

@@ -2090 +1873 @@
-			$sql .= " WHERE rowid = ".((int) $this->id);

+			$sql .= " WHERE rowid = ".$this->id;

@@ -2103,0 +1887,33 @@
+	 *     Link element with a project

+	 * 	   Override core function because of key name 'fk_project' used for this module

+	 *

+	 *     @param  int $projectid Project id to link element to

+	 *     @return int                        <0 if KO, >0 if OK

+	 */

+	public function setProject($projectid)

+	{

+		if (!$this->table_element) {

+			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);

+			return -1;

+		}

+

+		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;

+		if ($projectid) {

+			$sql .= ' SET fk_project = '.$projectid;

+		} else {

+			$sql .= ' SET fk_project = NULL';

+		}

+

+		$sql .= ' WHERE rowid = '.$this->id;

+

+		dol_syslog(get_class($this)."::setProject sql=".$sql);

+		if ($this->db->query($sql)) {

+			$this->fk_project = $projectid;

+			return 1;

+		} else {

+			dol_print_error($this->db);

+			return -1;

+		}

+	}

+

+	/**

@@ -2107 +1923 @@
-	 *     @return int                        Return integer <0 if KO, >0 if OK

+	 *     @return int                        <0 if KO, >0 if OK

@@ -2111,11 +1927,9 @@
-		if ($this->id) {

-			$sql = "UPDATE ".MAIN_DB_PREFIX."ticket";

-			$sql .= " SET fk_contract = ".($contractid > 0 ? $contractid : "null");

-			$sql .= " WHERE rowid = ".((int) $this->id);

-			dol_syslog(get_class($this).'::setContract sql='.$sql);

-			$resql = $this->db->query($sql);

-			if ($resql) {

-				return 1;

-			} else {

-				return -1;

-			}

+		if (!$this->table_element) {

+			dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);

+			return -1;

+		}

+

+		$result = $this->add_object_linked('contrat', $contractid);

+		if ($result) {

+			$this->fk_contract = $contractid;

+			return 1;

@@ -2122,0 +1937 @@
+			dol_print_error($this->db);

@@ -2140,8 +1955,7 @@
-	 *  Retrieve informations about internal contacts

-	 *

-	 *  @param    int     $status     Status of user or company

-	 *  @return array                 Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status

-	 */

-	public function getInfosTicketInternalContact($status = -1)

-	{

-		return $this->listeContact(-1, 'internal', 0, '', $status);

+	 * Retrieve informations about internal contacts

+	 *

+	 *  @return array       Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status

+	 */

+	public function getInfosTicketInternalContact()

+	{

+		return $this->listeContact(-1, 'internal');

@@ -2163,6 +1977,5 @@
-	 *  @param    int     $status     Status of user or company

-	 *  @return array                 Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status

-	 */

-	public function getInfosTicketExternalContact($status = -1)

-	{

-		return $this->listeContact(-1, 'external', 0, '', $status);

+	 *  @return array       Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status

+	 */

+	public function getInfosTicketExternalContact()

+	{

+		return $this->listeContact(-1, 'external');

@@ -2200,7 +2013,6 @@
-		$array_contact = $this->getIdTicketInternalContact();

-

-		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());

-

-		$array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());

-

-		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());

+		$array_contact = $this->getIdTicketInternalContact($exclude_self);

+

+		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self));

+

+		$array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact($exclude_self));

+		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self));

@@ -2220,3 +2032,2 @@
-		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());

-

-		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());

+		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self));

+		$array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self));

@@ -2226,0 +2038,119 @@
+	/**

+	 * Send message

+	 *

+	 *  @param  string $subject	  Subject

+	 *  @param  string $texte      Message to send

+	 *  @return int                <0 if KO, or number of changes if OK

+	 */

+	public function messageSend($subject, $texte)

+	{

+		global $conf, $langs, $mysoc, $dolibarr_main_url_root;

+

+		$langs->load("other");

+

+		dol_syslog(get_class($this)."::message_send action=$action, socid=$socid, texte=$texte, objet_type=$objet_type, objet_id=$objet_id, file=$file");

+

+		$internal_contacts = $this->getIdContact('internal', 'SUPPORTTEC');

+		$external_contacts = $this->getIdContact('external', 'SUPPORTTEC');

+

+		if ($result) {

+			$num = $this->db->num_rows($result);

+			$i = 0;

+			while ($i < $num) { // For each notification couple defined (third party/actioncode)

+				$obj = $this->db->fetch_object($result);

+

+				$sendto = $obj->firstname." ".$obj->lastname." <".$obj->email.">";

+				$actiondefid = $obj->adid;

+

+				if (dol_strlen($sendto))

+				{

+					include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';

+					$application = ($conf->global->MAIN_APPLICATION_TITLE ? $conf->global->MAIN_APPLICATION_TITLE : 'Dolibarr ERP/CRM');

+

+					$subject = '['.$application.'] '.$langs->transnoentitiesnoconv("DolibarrNotification");

+

+					$message = $langs->transnoentities("YouReceiveMailBecauseOfNotification", $application, $mysoc->name)."\n";

+					$message .= $langs->transnoentities("YouReceiveMailBecauseOfNotification2", $application, $mysoc->name)."\n";

+					$message .= "\n";

+					$message .= $texte;

+					// Add link

+					$link = '';

+					switch ($objet_type) {

+						case 'ficheinter':

+							$link = '/fichinter/card.php?id='.$objet_id;

+							break;

+						case 'propal':

+							$link = '/comm/propal.php?id='.$objet_id;

+							break;

+						case 'facture':

+							$link = '/compta/facture/card.php?facid='.$objet_id;

+							break;

+						case 'order':

+							$link = '/commande/card.php?facid='.$objet_id;

+							break;

+						case 'order_supplier':

+							$link = '/fourn/commande/card.php?facid='.$objet_id;

+							break;

+					}

+					// Define $urlwithroot

+					$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));

+					$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file

+					//$urlwithroot=DOL_MAIN_URL_ROOT;                        // This is to use same domain name than current

+					if ($link) {

+						$message .= "\n".$urlwithroot.$link;

+					}

+

+					$filename = basename($file);

+

+					$mimefile = dol_mimetype($file);

+

+					$msgishtml = 0;

+

+					$replyto = $conf->notification->email_from;

+

+					$message = dol_nl2br($message);

+

+					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 = '';

+					}

+					$mailfile = new CMailFile(

+						$subject,

+						$sendto,

+						$replyto,

+						$message,

+						array($file),

+						array($mimefile),

+						array($filename[count($filename) - 1]),

+						'',

+						'',

+						0,

+						$msgishtml

+					);

+

+					if ($mailfile->sendfile()) {

+						$now = dol_now();

+						$sendto = htmlentities($sendto);

+

+						$sql = "INSERT INTO ".MAIN_DB_PREFIX."notify (daten, fk_action, fk_contact, objet_type, objet_id, email)";

+						$sql .= " VALUES ('".$this->db->idate($now)."', ".$actiondefid.", ".$obj->cid.", '".$this->db->escape($objet_type)."', ".$objet_id.", '".$this->db->escape($obj->email)."')";

+						dol_syslog("Notify::send sql=".$sql);

+						if (!$this->db->query($sql)) {

+							dol_print_error($this->db);

+						}

+					} else {

+						$this->error = $mailfile->error;

+						//dol_syslog("Notify::send ".$this->error, LOG_ERR);

+					}

+					if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {

+						$conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;

+					}

+				}

+				$i++;

+			}

+			return $i;

+		} else {

+			$this->error = $this->db->error();

+			return -1;

+		}

+	}

@@ -2232,8 +2162,7 @@
-	 *    @param    int     $statusoflink   Status of lines to get (-1=all)

-	 *    @param    string  $source         Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)

-	 *    @param    int     $list           0:Return array contains all properties, 1:Return array contains just id

-	 *    @param    string  $code           Filter on this code of contact type ('SHIPPING', 'BILLING', ...)

-	 *    @param    int     $status         Status of user or company

-	 *    @return   array|int               Array of contacts

-	 */

-	public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)

+	 *    @param	int    	$status 	Status of lines to get (-1=all)

+	 *    @param	string 	$source 	Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)

+	 *    @param	int    	$list   	0:Return array contains all properties, 1:Return array contains just id

+	 *    @param    string  $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)

+	 *    @return 	array          		Array of contacts

+	 */

+	public function listeContact($status = -1, $source = 'external', $list = 0, $code = '')

@@ -2274 +2203 @@
-		$sql .= " WHERE ec.element_id = ".((int) $this->id);

+		$sql .= " WHERE ec.element_id =".$this->id;

@@ -2279,3 +2207,0 @@
-			if ($status >= 0) {

-				$sql .= " AND t.statut = ".((int) $status);

-			}

@@ -2286,7 +2211,0 @@
-			if ($status >= 0) {

-				$sql .= " AND t.statut = ".((int) $status);

-			}

-		}

-

-		if (!empty($code)) {

-			$sql .= " AND tc.code = '".$this->db->escape($code)."'";

@@ -2296,2 +2215,2 @@
-		if ($statusoflink >= 0) {

-			$sql .= " AND ec.statut = ".((int) $statusoflink);

+		if ($status >= 0) {

+			$sql .= " AND ec.statut = '".$status."'";

@@ -2355 +2274 @@
-		$modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');

+		$modele = empty($conf->global->TICKET_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKET_ADDON;

@@ -2373 +2292 @@
-			$modTicket = new $classname();

+			$modTicket = new $classname;

@@ -2409 +2328 @@
-						$file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1');	// To be sure data is stored in UTF8 in memory

+						$file = utf8_encode($file);

@@ -2410,0 +2330 @@
+					// To be sure data is stored in UTF8 in memory

@@ -2426,4 +2346,3 @@
-	 * @param	string		$forcetrackid	Force trackid used for $keytoavoidconflict into get_attached_files()

-	 * @return	array|int					Array with final path/name/mime of files.

-	 */

-	public function copyFilesForTicket($forcetrackid = null)

+	 * @return	array		Array with final path/name/mime of files.

+	 */

+	public function copyFilesForTicket()

@@ -2444 +2363 @@
-		$formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');

+

@@ -2447 +2366 @@
-		$filepath = $attachedfiles['paths'];	// path is for example user->dir_temp.'/'.$user->id.'/'...

+		$filepath = $attachedfiles['paths'];

@@ -2463 +2382,2 @@
-			if (is_file($destfile)) {

+			if (is_file($destfile))

+			{

@@ -2469,19 +2389,11 @@
-			$moreinfo = array('description'=>'File saved by copyFilesForTicket', 'src_object_type' => $this->element, 'src_object_id' => $this->id);

-			$res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1, $moreinfo);

-			if (!$res) {

-				// Move has failed

-				$this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);

-				return -1;

-			} else {

-				// If file is an image, we create thumbs

-				if (image_format_supported($destfile) == 1) {

-					// Create small thumbs for image (Ratio is near 16/9)

-					// Used on logon for example

-					$imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");

-					// Create mini thumbs for image (Ratio is near 16/9)

-					// Used on menu or for setup page for example

-					$imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");

-				}

-			}

-

-			// Clear variables into session

+			$res = dol_move($filepath[$i], $destfile, 0, 1);

+

+			if (image_format_supported($destfile) == 1) {

+				// Create small thumbs for image (Ratio is near 16/9)

+				// Used on logon for example

+				$imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");

+				// Create mini thumbs for image (Ratio is near 16/9)

+				// Used on menu or for setup page for example

+				$imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");

+			}

+

@@ -2498,59 +2410,10 @@
-	/**

-	 * Sets object to supplied categories.

-	 *

-	 * Deletes object from existing categories not supplied.

-	 * Adds it to non existing supplied categories.

-	 * Existing categories are left untouch.

-	 *

-	 * @param  int[]|int 	$categories 	Category or categories IDs

-	 * @return int							Return integer <0 if KO, >0 if OK

-	 */

-	public function setCategories($categories)

-	{

-		// Handle single category

-		if (!is_array($categories)) {

-			$categories = array($categories);

-		}

-

-		// Get current categories

-		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';

-		$c = new Categorie($this->db);

-		$existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');

-

-		// Diff

-		if (is_array($existing)) {

-			$to_del = array_diff($existing, $categories);

-			$to_add = array_diff($categories, $existing);

-		} else {

-			$to_del = array(); // Nothing to delete

-			$to_add = $categories;

-		}

-

-		// Process

-		foreach ($to_del as $del) {

-			if ($c->fetch($del) > 0) {

-				$c->del_type($this, Categorie::TYPE_TICKET);

-			}

-		}

-		foreach ($to_add as $add) {

-			if ($c->fetch($add) > 0) {

-				$c->add_type($this, Categorie::TYPE_TICKET);

-			}

-		}

-

-		return 1;

-	}

-

-	/**

-	 * Add new message on a ticket (private/public area).

-	 * Can also send it by email if GETPOST('send_email', 'int') is set. For such email, header and footer is added.

-	 *

-	 * @param   User    $user       	User for action

-	 * @param   string  $action     	Action string

-	 * @param   int     $private    	1=Message is private (must not be visible by external users)

-	 * @param   int     $public_area    0=Default,

-	 * 									1=If we are creating the message from a public area, so confirmation email will be sent to the author

-	 * 									and we can search contact from email to add it as contact of ticket if TICKET_ASSIGN_CONTACT_TO_MESSAGE is set

-	 * @return  int						Return integer <0 if KO, >= 0 if OK

-	 */

-	public function newMessage($user, &$action, $private = 1, $public_area = 0)

+

+	/**

+	 * Add new message on a ticket (private area). Can also send it be email if GETPOST('send_email', 'int') is set.

+	 *

+	 * @param   User    $user       User for action

+	 * @param   string  $action     Action string

+	 * @param   int     $private    1=Message is private. TODO Implement this. What does this means ?

+	 * @return  int

+	 */

+	public function newMessage($user, &$action, $private = 1)

@@ -2568 +2430,0 @@
-		$object->fetch_project();

@@ -2584 +2446 @@
-			$object->message = GETPOST("message", "restricthtml");

+			$object->message = GETPOST("message", "none");

@@ -2591,4 +2452,0 @@
-			if (is_numeric($resarray) && $resarray == -1) {

-				setEventMessages($object->error, $object->errors, 'errors');

-				return -1;

-			}

@@ -2600 +2458 @@
-			$id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email, $public_area);

+			$id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames);

@@ -2611,26 +2469,28 @@
-				//var_dump($_SESSION);

-				//var_dump($listofpaths);exit;

-

-				if (!empty($public_area)) {

-					/*

-					 * Message created from the Public interface

-					 *

-					 * Send emails to assigned users (public area notification)

-					 */

-					if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {

-						// Retrieve internal contact datas

-						$internal_contacts = $object->getInfosTicketInternalContact(1);

-

-						$assigned_user_dont_have_email = '';

-

-						$sendto = array();

-

-						if ($this->fk_user_assign > 0) {

-							$assigned_user = new User($this->db);

-							$assigned_user->fetch($this->fk_user_assign);

-							if (!empty($assigned_user->email)) {

-								$sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";

-							} else {

-								$assigned_user_dont_have_email = $assigned_user->getFullName($langs);

-							}

-						}

+				//var_dump($_SESSION); var_dump($listofpaths);exit;

+

+				/*

+                 * Send emails to internal users (linked contacts)

+                 */

+				if ($send_email > 0) {

+					// Retrieve internal contact datas

+					$internal_contacts = $object->getInfosTicketInternalContact();

+

+					$sendto = array();

+					if (is_array($internal_contacts) && count($internal_contacts) > 0) {

+						// altairis: set default subject

+						$label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;

+						$subject = GETPOST('subject', 'nohtml') ? GETPOST('subject', 'nohtml') : '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');

+

+						$message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);

+						$message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;

+

+						$message = $langs->trans('TicketMessageMailIntroText');

+						$message .= "\n\n";

+						$message .= GETPOST('message', 'restricthtml');

+

+						//  Coordonnées client

+						$message .= "\n\n";

+						$message .= "==============================================\n";

+						$message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';

+						$message .= !empty($object->thirdparty->town) ? "\n".$langs->trans('Town')." : ".$object->thirdparty->town : '';

+						$message .= !empty($object->thirdparty->phone) ? "\n".$langs->trans('Phone')." : ".$object->thirdparty->phone : '';

@@ -2640 +2500 @@
-							// Avoid duplicate notifications

+							// altairis: avoid duplicate notifications

@@ -2645,3 +2505,6 @@
-							// We check if the email address is not the assignee's address to prevent notification from being sent twice

-							if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {

-								$sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";

+							if ($info_sendto['email'] != '') {

+								if (!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";

+

+								//Contact type

+								$recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';

+								$message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient."\n" : '');

@@ -2650,7 +2513,10 @@
-

-						if (empty($sendto)) {

-							if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {

-								$sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');

-							} elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {

-								$sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');

-							}

+						$message .= "\n";

+						// URL ticket

+						$url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;

+

+						// altairis: make html link on url

+						$message .= "\n".$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>'."\n";

+

+						// Add global email address recipient

+						if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {

+							if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;

@@ -2659,7 +2525 @@
-						// Add global email address recipient

-						if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&

-							getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)

-						) {

-							$sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');

-						}

-

+						// altairis: dont try to send email if no recipient

@@ -2667,34 +2526,0 @@
-							$appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);

-

-							$subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');

-

-							// Message send

-							$message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));

-							$message .= '<br><br>';

-							$messagePost = GETPOST('message', 'restricthtml');

-							if (!dol_textishtml($messagePost)) {

-								$messagePost = dol_nl2br($messagePost);

-							}

-							$message .= $messagePost;

-

-							// Customer company infos

-							$message .= '<br><br>';

-							$message .= "==============================================";

-							$message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';

-							$message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';

-							$message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';

-

-							// Email send to

-							$message .= '<br><br>';

-							if (!empty($assigned_user_dont_have_email)) {

-								$message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;

-							}

-							foreach ($sendto as $val) {

-								$message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;

-							}

-

-							// URL ticket

-							$url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;

-							$message .= '<br><br>';

-							$message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';

-

@@ -2704 +2530 @@
-				} else {

+

@@ -2706,7 +2532,17 @@
-					 * Message send from the Backoffice / Private area

-					 *

-					 * Send emails to internal users (linked contacts) then, if private is not set, to external users (linked contacts or thirdparty email if no contact set)

-					 */

-					if ($send_email > 0) {

-						// Retrieve internal contact datas

-						$internal_contacts = $object->getInfosTicketInternalContact(1);

+                     * Send emails for externals users if not private (linked contacts)

+                     */

+					if (empty($object->private)) {

+						// Retrieve email of all contacts (external)

+						$external_contacts = $object->getInfosTicketExternalContact();

+

+						// If no contact, get email from thirdparty

+						if (is_array($external_contacts) && count($external_contacts) === 0) {

+							if (!empty($object->fk_soc)) {

+								$object->fetch_thirdparty($object->fk_soc);

+								$array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));

+								$external_contacts = array_merge($external_contacts, $array_company);

+							} elseif (empty($object->fk_soc) && !empty($object->origin_email)) {

+								$array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));

+								$external_contacts = array_merge($external_contacts, $array_external);

+							}

+						}

@@ -2715,28 +2551,15 @@
-						if (is_array($internal_contacts) && count($internal_contacts) > 0) {

-							// Set default subject

-							$appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);

-

-							$subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');

-

-							$message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);

-							$message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');

-

-							$message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));

-							$message .= '<br><br>';

-							$messagePost = GETPOST('message', 'restricthtml');

-							if (!dol_textishtml($messagePost)) {

-								$messagePost = dol_nl2br($messagePost);

-							}

-							$message .= $messagePost;

-

-							// Data about customer

-							$message .= '<br><br>';

-							$message .= "==============================================<br>";

-							$message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';

-							$message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';

-							$message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';

-

-							// Build array to display recipient list

-							foreach ($internal_contacts as $key => $info_sendto) {

-								// Avoid duplicate notifications

-								if ($info_sendto['id'] == $user->id) {

+						if (is_array($external_contacts) && count($external_contacts) > 0) {

+							// altairis: get default subject for email to external contacts

+							$label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;

+							$subject = GETPOST('subject') ? GETPOST('subject') : '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');

+

+							$message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKET_MESSAGE_MAIL_INTRO;

+							$message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;

+

+							// We put intro after

+							$message = GETPOST('message');

+							$message .= "\n\n";

+

+							foreach ($external_contacts as $key => $info_sendto) {

+								// altairis: avoid duplicate emails to external contacts

+								if ($info_sendto['id'] == $user->contactid) {

@@ -2746,6 +2569,3 @@
-								if ($info_sendto['email'] != '') {

-									if (!empty($info_sendto['email'])) {

-										$sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";

-									}

-

-									// Contact type

+								if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {

+									if (!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";

+

@@ -2753 +2573 @@
-									$message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');

+									$message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient."\n" : '');

@@ -2756,12 +2576,14 @@
-							$message .= '<br>';

-							// URL ticket

-							$url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;

-

-							// Add html link on url

-							$message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';

-

-							// Add global email address recipient

-							if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {

-								if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {

-									$sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');

-								}

+

+							// If public interface is not enable, use link to internal page into mail

+							$url_public_ticket = (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) ?

+								(!empty($conf->global->TICKET_URL_PUBLIC_INTERFACE) ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/view.php' : dol_buildpath('/public/ticket/view.php', 2)) : dol_buildpath('/ticket/card.php', 2)).'?track_id='.$object->track_id;

+							$message .= "\n".$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a>'."\n";

+

+							// Build final message

+							$message = $message_intro.$message;

+

+							// Add signature

+							$message .= '<br>'.$message_signature;

+

+							if (!empty($object->origin_email)) {

+								$sendto[] = $object->origin_email;

@@ -2770 +2592,12 @@
-							// dont try to send email if no recipient

+							if ($object->fk_soc > 0 && !in_array($object->origin_email, $sendto)) {

+								$object->socid = $object->fk_soc;

+								$object->fetch_thirdparty();

+								if (!empty($object->thirdparty->email)) $sendto[] = $object->thirdparty->email;

+							}

+

+							// altairis: Add global email address reciepient

+							if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {

+								if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;

+							}

+

+							// altairis: dont try to send email when no recipient

@@ -2775,102 +2607,0 @@
-

-						/*

-						 * Send emails for externals users if not private (linked contacts)

-						 */

-						if (empty($object->private)) {

-							// Retrieve email of all contacts (external)

-							$external_contacts = $object->getInfosTicketExternalContact(1);

-

-							// If no contact, get email from thirdparty

-							if (is_array($external_contacts) && count($external_contacts) === 0) {

-								if (!empty($object->fk_soc)) {

-									$object->fetch_thirdparty($object->fk_soc);

-									$array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));

-									$external_contacts = array_merge($external_contacts, $array_company);

-								} elseif (empty($object->fk_soc) && !empty($object->origin_email)) {

-									$array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));

-									$external_contacts = array_merge($external_contacts, $array_external);

-								}

-							}

-

-							$sendto = array();

-							if (is_array($external_contacts) && count($external_contacts) > 0) {

-								// Get default subject for email to external contacts

-								$appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);

-

-								$subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');

-

-								$message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');

-								$message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');

-								if (!dol_textishtml($message_intro)) {

-									$message_intro = dol_nl2br($message_intro);

-								}

-								if (!dol_textishtml($message_signature)) {

-									$message_signature = dol_nl2br($message_signature);

-								}

-

-								// We put intro after

-								$messagePost = GETPOST('message', 'restricthtml');

-								if (!dol_textishtml($messagePost)) {

-									$messagePost = dol_nl2br($messagePost);

-								}

-								$message = $messagePost;

-								$message .= '<br><br>';

-

-								foreach ($external_contacts as $key => $info_sendto) {

-									// altairis: avoid duplicate emails to external contacts

-									if ($info_sendto['id'] == $user->contact_id) {

-										continue;

-									}

-

-									if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {

-										if (!empty($info_sendto['email'])) {

-											$sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";

-										}

-

-										$recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';

-										$message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');

-									}

-								}

-

-								// If public interface is not enable, use link to internal page into mail

-								$url_public_ticket = (getDolGlobalInt('TICKET_ENABLE_PUBLIC_INTERFACE') ?

-										(getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE') !== '' ? getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE') . '/view.php' : dol_buildpath('/public/ticket/view.php', 2)) : dol_buildpath('/ticket/card.php', 2)).'?track_id='.$object->track_id;

-

-								$message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';

-

-								// Build final message

-								$message = $message_intro.'<br><br>'.$message;

-

-								// Add signature

-								$message .= '<br>'.$message_signature;

-

-								if (!empty($object->origin_email)) {

-									$sendto[$object->origin_email] = $object->origin_email;

-								}

-

-								if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {

-									$object->socid = $object->fk_soc;

-									$object->fetch_thirdparty();

-									if (!empty($object->thirdparty->email)) {

-										$sendto[$object->thirdparty->email] = $object->thirdparty->email;

-									}

-								}

-

-								// Add global email address recipient

-								if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {

-									if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {

-										$sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');

-									}

-								}

-

-								// Dont try to send email when no recipient

-								if (!empty($sendto)) {

-									$result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);

-									if ($result) {

-										// update last_msg_sent date (for last message sent to external users)

-										$this->date_last_msg_sent = dol_now();

-										$this->update($user, 1);	// disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.

-									}

-								}

-							}

-						}

@@ -2880,7 +2611,4 @@
-				// Set status back to "In progress" if not set yet, but only if internal user and not a private message

-				// Or set status to "In porgress" if the client has answered and if the ticket has started

-				// So we are sure to leave the STATUS_DRAFT, STATUS_NEED_INFO.

-				if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||

-					($object->status > self::STATUS_IN_PROGRESS && $public_area)

-				) {

-					$object->setStatut($object::STATUS_IN_PROGRESS);

+				// Set status to "answered" if not set yet, but only if internal user

+				if ($object->fk_statut < 3 && !$user->socid)

+				{

+					$object->setStatut(3);

@@ -2887,0 +2616 @@
+

@@ -2905 +2634 @@
-	 * @param int    $send_internal_cc 	  Receive a copy on internal email (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM')

+	 * @param int    $send_internal_cc 	  Receive a copy on internal email ($conf->global->TICKET_NOTIFICATION_EMAIL_FROM)

@@ -2910 +2639 @@
-	 * @return boolean     					True if mail sent to at least one receiver, false otherwise

+	 * @return void

@@ -2914,3 +2643,3 @@
-		global $conf, $langs, $user;

-

-		if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {

+		global $conf, $langs;

+

+		if ($conf->global->TICKET_DISABLE_ALL_MAILS) {

@@ -2918 +2647 @@
-			return false;

+			return '';

@@ -2928,5 +2657,4 @@
-			$array_receiver = $this->getInfosTicketInternalContact(1);

-			$array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));

-		}

-

-		$sendtocc = "";

+			$array_receiver = $this->getInfosTicketInternalContact();

+			$array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact());

+		}

+

@@ -2934,7 +2662,4 @@
-			$sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');

-		} else {

-			$sendtocc = '';

-		}

-

-		$from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');

-		$is_sent = false;

+			$sendtocc = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;

+		}

+

+		$from = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;

@@ -2942 +2667,2 @@
-			foreach ($array_receiver as $key => $receiver) {

+			foreach ($array_receiver as $key => $receiver)

+			{

@@ -2948,4 +2674,5 @@
-				// Send email

-

-				$old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');

-				if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {

+				$message_to_send = dol_nl2br($message);

+

+				// Envoi du mail

+				if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {

+					$old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;

@@ -2954,3 +2680,0 @@
-

-				$upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';

-

@@ -2959,9 +2683 @@
-

-				$moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";

-				if (!empty($this->email_msgid)) {

-					// We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).

-					$moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";

-				}

-

-				$mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);

-

+				$mailfile = new CMailFile($subject, $receiver, $from, $message_to_send, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid);

@@ -2974 +2689,0 @@
-						$is_sent = true;

@@ -2985,2 +2700 @@
-

-				if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {

+				if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {

@@ -2994,2 +2707,0 @@
-

-		return $is_sent;

@@ -3000,5 +2712,5 @@
-	 *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)

-	 *

-	 *  @param          User	$user   Object user

-	 *  @param          int		$mode   "opened" for askprice to close, "signed" for proposal to invoice

-	 *  @return         WorkboardResponse|int             Return integer <0 if KO, WorkboardResponse if OK

+	 *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)

+	 *

+	 *      @param          User	$user   Object user

+	 *      @param          int		$mode   "opened" for askprice to close, "signed" for proposal to invoice

+	 *      @return         int             <0 if KO, >0 if OK

@@ -3009 +2721 @@
-		global $user, $langs;

+		global $conf, $user, $langs;

@@ -3012,2 +2724,2 @@
-		$delay_warning = 0;

-

+

+		$this->nbtodo = $this->nbtodolate = 0;

@@ -3018 +2730,2 @@
-		if (isModEnabled('societe') && !$user->hasRight('societe', 'client', 'voir') && !$user->socid) {

+		if (!$user->rights->societe->client->voir && !$user->socid)

+		{

@@ -3020 +2733 @@
-			$sql .= " WHERE sc.fk_user = ".((int) $user->id);

+			$sql .= " WHERE sc.fk_user = ".$user->id;

@@ -3024,6 +2737,2 @@
-		if ($mode == 'opened') {

-			$sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";

-		}

-		if ($user->socid) {

-			$sql .= " AND p.fk_soc = ".((int) $user->socid);

-		}

+		if ($mode == 'opened') $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";

+		if ($user->socid) $sql .= " AND p.fk_soc = ".$user->socid;

@@ -3032 +2741,2 @@
-		if ($resql) {

+		if ($resql)

+		{

@@ -3051 +2761,2 @@
-			while ($obj = $this->db->fetch_object($resql)) {

+			while ($obj = $this->db->fetch_object($resql))

+			{

@@ -3053,3 +2764,5 @@
-				if ($mode == 'opened') {

-					$datelimit = $this->db->jdate($obj->datec) + $delay_warning;

-					if ($datelimit < $now) {

+				if ($mode == 'opened')

+				{

+					$datelimit = $this->db->jdate($obj->datefin);

+					if ($datelimit < ($now - $delay_warning))

+					{

@@ -3061 +2774,3 @@
-		} else {

+		}

+		else

+		{

@@ -3071 +2786 @@
-	 *      @return     int         Return integer <0 if ko, >0 if ok

+	 *      @return     int         <0 if ko, >0 if ok

@@ -3084 +2799,2 @@
-		if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {

+		if (!$user->rights->societe->client->voir && !$user->socid)

+		{

@@ -3086 +2802 @@
-			$sql .= " WHERE sc.fk_user = ".((int) $user->id);

+			$sql .= " WHERE sc.fk_user = ".$user->id;

@@ -3092 +2808,2 @@
-		if ($resql) {

+		if ($resql)

+		{

@@ -3094 +2811,2 @@
-			while ($obj = $this->db->fetch_object($resql)) {

+			while ($obj = $this->db->fetch_object($resql))

+			{

@@ -3099 +2817,3 @@
-		} else {

+		}

+		else

+		{

@@ -3105,54 +2824,0 @@
-

-	/**

-	 * Function used to replace a thirdparty id with another one.

-	 *

-	 * @param DoliDB 	$db 			Database handler

-	 * @param int 		$origin_id 		Old thirdparty id

-	 * @param int 		$dest_id 		New thirdparty id

-	 * @return bool

-	 */

-	public static function replaceThirdparty($db, $origin_id, $dest_id)

-	{

-		$tables = array('ticket');

-

-		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);

-	}

-

-	/**

-	 *	Return clicable link of object (with eventually picto)

-	 *

-	 *	@param      string	    $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)

-	 *  @param		array		$arraydata				Array of data

-	 *  @return		string								HTML Code for Kanban thumb.

-	 */

-	public function getKanbanView($option = '', $arraydata = null)

-	{

-		global $langs;

-

-		$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);

-

-		$return = '<div class="box-flex-item box-flex-grow-zero">';

-		$return .= '<div class="info-box info-box-sm">';

-		$return .= '<span class="info-box-icon bg-infobox-action">';

-		$return .= img_picto('', $this->picto);

-		$return .= '</span>';

-		$return .= '<div class="info-box-content">';

-		$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';

-		if ($selected >= 0) {

-			$return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';

-		}

-		if (!empty($arraydata['user_assignment'])) {

-			$return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';

-		}

-		if (property_exists($this, 'type_code') && !empty($this->type_code)) {

-			$return .= '<br>';

-			$return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';

-		}

-		if (method_exists($this, 'getLibStatut')) {

-			$return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';

-		}

-		$return .= '</div>';

-		$return .= '</div>';

-		$return .= '</div>';

-		return $return;

-	}

@@ -3159,0 +2826,128 @@
+

+

+/**

+ * Ticket line Class

+ */

+class TicketsLine

+{

+	/**

+	 * @var int ID

+	 * @deprecated

+	 */

+	public $rowid;

+

+	/**

+	 * @var int ID

+	 */

+	public $id;

+

+	/**

+	 * @var string  $ref    Ticket reference

+	 */

+	public $ref;

+

+	/**

+	 * Hash to identify ticket

+	 */

+	public $track_id;

+

+	/**

+	 * @var int Thirdparty ID

+	 */

+	public $fk_soc;

+

+	/**

+	 * Project ID

+	 */

+	public $fk_project;

+

+	/**

+	 * Person email who have create ticket

+	 */

+	public $origin_email;

+

+	/**

+	 * User id who have create ticket

+	 */

+	public $fk_user_create;

+

+	/**

+	 * User id who have ticket assigned

+	 */

+	public $fk_user_assign;

+

+	/**

+	 * Ticket subject

+	 */

+	public $subject;

+

+	/**

+	 * Ticket message

+	 */

+	public $message;

+

+	/**

+	 * Ticket statut

+	 */

+	public $fk_statut;

+

+	/**

+	 * State resolution

+	 */

+	public $resolution;

+

+	/**

+	 * Progress in percent

+	 */

+	public $progress;

+

+	/**

+	 * Duration for ticket

+	 */

+	public $timing;

+

+	/**

+	 * Type code

+	 */

+	public $type_code;

+

+	/**

+	 * Category code

+	 */

+	public $category_code;

+

+	/**

+	 * Severity code

+	 */

+	public $severity_code;

+

+	/**

+	 * Type label

+	 */

+	public $type_label;

+

+	/**

+	 * Category label

+	 */

+	public $category_label;

+

+	/**

+	 * Severity label

+	 */

+	public $severity_label;

+

+	/**

+	 * Creation date

+	 */

+	public $datec = '';

+

+	/**

+	 * Read date

+	 */

+	public $date_read = '';

+

+	/**

+	 * Close ticket date

+	 */

+	public $date_close = '';

+}

--- /tmp/dsg/dolibarr/htdocs/ticket/class/github_19.0.3_ticketstats.class.php
+++ /tmp/dsg/dolibarr/htdocs/ticket/class/client_ticketstats.class.php
@@ -32,4 +32,4 @@
-	/**

-	 * @var string Name of table without prefix where object is stored

-	 */

-	public $table_element;

+    /**

+     * @var string Name of table without prefix where object is stored

+     */

+    public $table_element;

@@ -37,2 +37,2 @@
-	public $socid;

-	public $userid;

+    public $socid;

+    public $userid;

@@ -40,3 +40,3 @@
-	public $from;

-	public $field;

-	public $where;

+    public $from;

+    public $field;

+    public $where;

@@ -44,11 +44,11 @@
-	/**

-	 * Constructor

-	 *

-	 * @param  DoliDB $db     Database handler

-	 * @param  int    $socid  Id third party

-	 * @param  mixed  $userid Id user for filter or array of user ids

-	 * @return void

-	 */

-	public function __construct($db, $socid = 0, $userid = 0)

-	{

-		global $conf;

+    /**

+     * Constructor

+     *

+     * @param  DoliDB $db     Database handler

+     * @param  int    $socid  Id third party

+     * @param  mixed  $userid Id user for filter or array of user ids

+     * @return void

+     */

+    public function __construct($db, $socid = 0, $userid = 0)

+    {

+        global $conf;

@@ -56,3 +56,3 @@
-		$this->db = $db;

-		$this->socid = $socid;

-		$this->userid = $userid;

+        $this->db = $db;

+        $this->socid = $socid;

+        $this->userid = $userid;

@@ -60,3 +60,3 @@
-		$object = new Ticket($this->db);

-		$this->from = MAIN_DB_PREFIX.$object->table_element;

-		$this->field = 'timing';

+        $object = new Ticket($this->db);

+        $this->from = MAIN_DB_PREFIX.$object->table_element;

+        $this->field = 'timing';

@@ -64,11 +64,11 @@
-		$this->where = " fk_statut > 0";

-		$this->where .= " AND entity = ".((int) $conf->entity);

-		if ($this->socid > 0) {

-			$this->where .= " AND fk_soc = ".((int) $this->socid);

-		}

-		if (is_array($this->userid) && count($this->userid) > 0) {

-			$this->where .= ' AND fk_user_create IN ('.$this->db->sanitize(join(',', $this->userid)).')';

-		} elseif ($this->userid > 0) {

-			$this->where .= " AND fk_user_create = ".((int) $this->userid);

-		}

-	}

+        $this->where = " fk_statut > 0";

+        $this->where .= " AND entity = ".$conf->entity;

+        if ($this->socid > 0) {

+            $this->where .= " AND fk_soc = ".$this->socid;

+        }

+        if (is_array($this->userid) && count($this->userid) > 0) {

+            $this->where .= ' AND fk_user_create IN ('.join(',', $this->userid).')';

+        } elseif ($this->userid > 0) {

+            $this->where .= ' AND fk_user_create = '.$this->userid;

+        }

+    }

@@ -76,11 +76,11 @@
-	/**

-	 *     Renvoie le nombre de tickets par annee

-	 *

-	 *    @return array    Array of values

-	 */

-	public function getNbByYear()

-	{

-		$sql = "SELECT YEAR(datec) as dm, count(*)";

-		$sql .= " FROM ".$this->from;

-		$sql .= " GROUP BY dm DESC";

-		$sql .= " WHERE ".$this->where;

+    /**

+     *     Renvoie le nombre de tickets par annee

+     *

+     *    @return array    Array of values

+     */

+    public function getNbByYear()

+    {

+        $sql = "SELECT YEAR(datec) as dm, count(*)";

+        $sql .= " FROM ".$this->from;

+        $sql .= " GROUP BY dm DESC";

+        $sql .= " WHERE ".$this->where;

@@ -88,2 +88,2 @@
-		return $this->_getNbByYear($sql);

-	}

+        return $this->_getNbByYear($sql);

+    }

@@ -91,15 +91,14 @@
-	/**

-	 *  Return the number of tickets per month for a given year

-	 *

-	 *  @param  string 	$year 		Year to scan

-	 *	@param	int		$format		0=Label of abscissa is a translated text, 1=Label of abscissa is month number, 2=Label of abscissa is first letter of month

-	 *  @return array            	Array of values

-	 */

-	public function getNbByMonth($year, $format = 0)

-	{

-		$sql = "SELECT MONTH(datec) as dm, count(*)";

-		$sql .= " FROM ".$this->from;

-		$sql .= " WHERE YEAR(datec) = ".((int) $year);

-		$sql .= " AND ".$this->where;

-		$sql .= " GROUP BY dm";

-		$sql .= $this->db->order('dm', 'DESC');

+    /**

+     *     Renvoie le nombre de facture par mois pour une annee donnee

+     *

+     *    @param  string $year Year to scan

+     *    @return array            Array of values

+     */

+    public function getNbByMonth($year)

+    {

+        $sql = "SELECT MONTH(datec) as dm, count(*)";

+        $sql .= " FROM ".$this->from;

+        $sql .= " WHERE YEAR(datec) = ".$year;

+        $sql .= " AND ".$this->where;

+        $sql .= " GROUP BY dm";

+        $sql .= $this->db->order('dm', 'DESC');

@@ -107,4 +106,4 @@
-		$res = $this->_getNbByMonth($year, $sql, $format);

-		//var_dump($res);print '<br>';

-		return $res;

-	}

+        $res = $this->_getNbByMonth($year, $sql);

+        //var_dump($res);print '<br>';

+        return $res;

+    }

@@ -112,15 +111,14 @@
-	/**

-	 *  Return th eamount of tickets for a month and a given year

-	 *

-	 *  @param  int 	$year 		Year to scan

-	 *	@param	int		$format		0=Label of abscissa is a translated text, 1=Label of abscissa is month number, 2=Label of abscissa is first letter of month

-	 *  @return array               Array of values

-	 */

-	public function getAmountByMonth($year, $format = 0)

-	{

-		$sql = "SELECT date_format(datec,'%m') as dm, sum(".$this->field.")";

-		$sql .= " FROM ".$this->from;

-		$sql .= " WHERE date_format(datec,'%Y') = '".$this->db->escape($year)."'";

-		$sql .= " AND ".$this->where;

-		$sql .= " GROUP BY dm";

-		$sql .= $this->db->order('dm', 'DESC');

+    /**

+     *     Renvoie le montant de facture par mois pour une annee donnee

+     *

+     *    @param  int $year Year to scan

+     *    @return array                Array of values

+     */

+    public function getAmountByMonth($year)

+    {

+        $sql = "SELECT date_format(datec,'%m') as dm, sum(".$this->field.")";

+        $sql .= " FROM ".$this->from;

+        $sql .= " WHERE date_format(datec,'%Y') = '".$year."'";

+        $sql .= " AND ".$this->where;

+        $sql .= " GROUP BY dm";

+        $sql .= $this->db->order('dm', 'DESC');

@@ -128,4 +126,4 @@
-		$res = $this->_getAmountByMonth($year, $sql, $format);

-		//var_dump($res);print '<br>';

-		return $res;

-	}

+        $res = $this->_getAmountByMonth($year, $sql);

+        //var_dump($res);print '<br>';

+        return $res;

+    }

@@ -133,14 +131,14 @@
-	/**

-	 *    Return average amount

-	 *

-	 *    @param  int $year Year to scan

-	 *    @return array                Array of values

-	 */

-	public function getAverageByMonth($year)

-	{

-		$sql = "SELECT date_format(datec,'%m') as dm, avg(".$this->field.")";

-		$sql .= " FROM ".$this->from;

-		$sql .= " WHERE date_format(datec,'%Y') = '".$this->db->escape($year)."'";

-		$sql .= " AND ".$this->where;

-		$sql .= " GROUP BY dm";

-		$sql .= $this->db->order('dm', 'DESC');

+    /**

+     *    Return average amount

+     *

+     *    @param  int $year Year to scan

+     *    @return array                Array of values

+     */

+    public function getAverageByMonth($year)

+    {

+        $sql = "SELECT date_format(datec,'%m') as dm, avg(".$this->field.")";

+        $sql .= " FROM ".$this->from;

+        $sql .= " WHERE date_format(datec,'%Y') = '".$year."'";

+        $sql .= " AND ".$this->where;

+        $sql .= " GROUP BY dm";

+        $sql .= $this->db->order('dm', 'DESC');

@@ -148,2 +146,2 @@
-		return $this->_getAverageByMonth($year, $sql);

-	}

+        return $this->_getAverageByMonth($year, $sql);

+    }

@@ -151,12 +149,12 @@
-	/**

-	 *    Return nb, total and average

-	 *

-	 *    @return array                Array of values

-	 */

-	public function getAllByYear()

-	{

-		$sql = "SELECT date_format(datec,'%Y') as year, count(*) as nb, sum(".$this->field.") as total, avg(".$this->field.") as avg";

-		$sql .= " FROM ".$this->from;

-		$sql .= " WHERE ".$this->where;

-		$sql .= " GROUP BY year";

-		$sql .= $this->db->order('year', 'DESC');

+    /**

+     *    Return nb, total and average

+     *

+     *    @return array                Array of values

+     */

+    public function getAllByYear()

+    {

+        $sql = "SELECT date_format(datec,'%Y') as year, count(*) as nb, sum(".$this->field.") as total, avg(".$this->field.") as avg";

+        $sql .= " FROM ".$this->from;

+        $sql .= " WHERE ".$this->where;

+        $sql .= " GROUP BY year";

+        $sql .= $this->db->order('year', 'DESC');

@@ -164,2 +162,2 @@
-		return $this->_getAllByYear($sql);

-	}

+        return $this->_getAllByYear($sql);

+    }

--- /tmp/dsg/dolibarr/htdocs/ticket/class/github_19.0.3_utils_diff.class.php
+++ /tmp/dsg/dolibarr/htdocs/ticket/class/client_utils_diff.class.php
@@ -0,0 +1,394 @@
+<?php

+/* Copyright (C) 2016      Jean-François Ferry  <hello@librethic.io>

+ *

+ * A class containing a diff implementation

+ *

+ * Created by Stephen Morley - http://stephenmorley.org/ - and released under the

+ * terms of the CC0 1.0 Universal legal code:

+ *

+ * http://creativecommons.org/publicdomain/zero/1.0/legalcode

+ */

+

+

+/**

+ * A class containing functions for computing diffs and formatting the output.

+ */

+class Diff

+{

+	// define the constants

+	const UNMODIFIED = 0;

+	const DELETED = 1;

+	const INSERTED = 2;

+

+	/**

+	 * Returns the diff for two strings. The return value is an array, each of

+     * whose values is an array containing two values: a line (or character, if

+     * $compareCharacters is true), and one of the constants DIFF::UNMODIFIED (the

+     * line or character is in both strings), DIFF::DELETED (the line or character

+     * is only in the first string), and DIFF::INSERTED (the line or character is

+     * only in the second string). The parameters are:

+     *

+     * @param	string	$string1            First string

+     * @param	string	$string2            Second string

+     * @param	string	$compareCharacters  true to compare characters, and false to compare lines; this optional parameter defaults to false

+     * @return	array						Array of diff

+     */

+	public static function compare($string1, $string2, $compareCharacters = false)

+	{

+		// initialise the sequences and comparison start and end positions

+		$start = 0;

+		if ($compareCharacters) {

+			$sequence1 = $string1;

+			$sequence2 = $string2;

+			$end1 = strlen($string1) - 1;

+			$end2 = strlen($string2) - 1;

+		} else {

+			$sequence1 = preg_split('/\R/', $string1);

+			$sequence2 = preg_split('/\R/', $string2);

+			$end1 = count($sequence1) - 1;

+			$end2 = count($sequence2) - 1;

+		}

+

+		// skip any common prefix

+		while ($start <= $end1 && $start <= $end2

+			&& $sequence1[$start] == $sequence2[$start]) {

+			$start++;

+		}

+

+		// skip any common suffix

+		while ($end1 >= $start && $end2 >= $start

+			&& $sequence1[$end1] == $sequence2[$end2]) {

+			$end1--;

+			$end2--;

+		}

+

+		// compute the table of longest common subsequence lengths

+		$table = self::computeTable($sequence1, $sequence2, $start, $end1, $end2);

+

+		// generate the partial diff

+		$partialDiff = self::generatePartialDiff($table, $sequence1, $sequence2, $start);

+

+		// generate the full diff

+		$diff = array();

+		for ($index = 0; $index < $start; $index++) {

+			$diff[] = array($sequence1[$index], self::UNMODIFIED);

+		}

+		while (count($partialDiff) > 0) {

+			$diff[] = array_pop($partialDiff);

+		}

+

+		$end2 = ($compareCharacters ? strlen($sequence1) : count($sequence1));

+		for ($index = $end1 + 1; $index < $end2; $index++)

+		{

+			$diff[] = array($sequence1[$index], self::UNMODIFIED);

+		}

+

+		// return the diff

+		return $diff;

+	}

+

+	/**

+	 * Returns the diff for two files. The parameters are:

+     *

+     * @param	string	$file1              Path to the first file

+     * @param	string	$file2              Path to the second file

+     * @param	boolean	$compareCharacters  true to compare characters, and false to compare lines; this optional parameter defaults to false

+     * @return	array						Array of diff

+     */

+	public static function compareFiles(

+		$file1,

+		$file2,

+		$compareCharacters = false

+	) {

+

+		// return the diff of the files

+		return self::compare(

+			file_get_contents($file1),

+			file_get_contents($file2),

+			$compareCharacters

+		);

+	}

+

+	/**

+	 * Returns the table of longest common subsequence lengths for the specified sequences. The parameters are:

+     *

+     * @param	string	$sequence1 	the first sequence

+     * @param	string	$sequence2 	the second sequence

+     * @param	string	$start     	the starting index

+     * @param	string	$end1      	the ending index for the first sequence

+     * @param	string	$end2      	the ending index for the second sequence

+     * @return	array				array of diff

+     */

+	private static function computeTable($sequence1, $sequence2, $start, $end1, $end2)

+	{

+		// determine the lengths to be compared

+		$length1 = $end1 - $start + 1;

+		$length2 = $end2 - $start + 1;

+

+		// initialise the table

+		$table = array(array_fill(0, $length2 + 1, 0));

+

+		// loop over the rows

+		for ($index1 = 1; $index1 <= $length1; $index1++) {

+			// create the new row

+			$table[$index1] = array(0);

+

+			// loop over the columns

+			for ($index2 = 1; $index2 <= $length2; $index2++) {

+				// store the longest common subsequence length

+				if ($sequence1[$index1 + $start - 1] == $sequence2[$index2 + $start - 1]

+				) {

+					$table[$index1][$index2] = $table[$index1 - 1][$index2 - 1] + 1;

+				} else {

+					$table[$index1][$index2] = max($table[$index1 - 1][$index2], $table[$index1][$index2 - 1]);

+				}

+			}

+		}

+

+		// return the table

+		return $table;

+	}

+

+	/**

+	 * Returns the partial diff for the specificed sequences, in reverse order.

+	 * The parameters are:

+     *

+     * @param	string	$table     	the table returned by the computeTable function

+     * @param	string	$sequence1 	the first sequence

+     * @param	string	$sequence2 	the second sequence

+     * @param	string	$start     	the starting index

+     * @return	array				array of diff

+     */

+	private static function generatePartialDiff($table,	$sequence1,	$sequence2, $start)

+	{

+		//  initialise the diff

+		$diff = array();

+

+		// initialise the indices

+		$index1 = count($table) - 1;

+		$index2 = count($table[0]) - 1;

+

+		// loop until there are no items remaining in either sequence

+		while ($index1 > 0 || $index2 > 0) {

+			// check what has happened to the items at these indices

+			if ($index1 > 0 && $index2 > 0

+				&& $sequence1[$index1 + $start - 1] == $sequence2[$index2 + $start - 1]

+			) {

+				// update the diff and the indices

+				$diff[] = array($sequence1[$index1 + $start - 1], self::UNMODIFIED);

+				$index1--;

+				$index2--;

+			} elseif ($index2 > 0

+				&& $table[$index1][$index2] == $table[$index1][$index2 - 1]

+			) {

+				// update the diff and the indices

+				$diff[] = array($sequence2[$index2 + $start - 1], self::INSERTED);

+				$index2--;

+			} else {

+				// update the diff and the indices

+				$diff[] = array($sequence1[$index1 + $start - 1], self::DELETED);

+				$index1--;

+			}

+		}

+

+		// return the diff

+		return $diff;

+	}

+

+	/**

+	 * Returns a diff as a string, where unmodified lines are prefixed by '  ',

+     * deletions are prefixed by '- ', and insertions are prefixed by '+ '. The

+     * parameters are:

+     *

+     * @param	array	$diff      	the diff array

+     * @param	string	$separator 	the separator between lines; this optional parameter defaults to "\n"

+     * @return	string				String

+     */

+	public static function toString($diff, $separator = "\n")

+	{

+		// initialise the string

+		$string = '';

+

+		// loop over the lines in the diff

+		foreach ($diff as $line) {

+			// extend the string with the line

+			switch ($line[1]) {

+				case self::UNMODIFIED:

+					$string .= '  '.$line[0];

+					break;

+				case self::DELETED:

+					$string .= '- '.$line[0];

+					break;

+				case self::INSERTED:

+					$string .= '+ '.$line[0];

+					break;

+			}

+

+			// extend the string with the separator

+			$string .= $separator;

+		}

+

+		// return the string

+		return $string;

+	}

+

+	/**

+	 * Returns a diff as an HTML string, where unmodified lines are contained

+     * within 'span' elements, deletions are contained within 'del' elements, and

+     * insertions are contained within 'ins' elements. The parameters are:

+     *

+     * @param	string	$diff      	the diff array

+     * @param	string	$separator 	the separator between lines; this optional parameter defaults to '<br>'

+     * @return	string				HTML string

+     */

+	public static function toHTML($diff, $separator = '<br>')

+	{

+		// initialise the HTML

+		$html = '';

+

+		// loop over the lines in the diff

+		foreach ($diff as $line) {

+			// extend the HTML with the line

+			switch ($line[1]) {

+				case self::UNMODIFIED:

+					$element = 'span';

+					break;

+				case self::DELETED:

+					$element = 'del';

+					break;

+				case self::INSERTED:

+					$element = 'ins';

+					break;

+			}

+			$html .=

+			'<'.$element.'>'

+			. htmlspecialchars($line[0])

+				. '</'.$element.'>';

+

+			// extend the HTML with the separator

+			$html .= $separator;

+		}

+

+		// return the HTML

+		return $html;

+	}

+

+	/**

+	 * Returns a diff as an HTML table. The parameters are:

+     *

+     * @param	string	$diff        	the diff array

+     * @param	string	$indentation 	indentation to add to every line of the generated HTML; this optional parameter defaults to ''

+     * @param	string	$separator   	the separator between lines; this optional parameter defaults to '<br>'

+     * @return	string					HTML string

+     */

+	public static function toTable($diff, $indentation = '', $separator = '<br>')

+	{

+		// initialise the HTML

+		$html = $indentation."<table class=\"diff\">\n";

+

+		// loop over the lines in the diff

+		$index = 0;

+		while ($index < count($diff)) {

+			// determine the line type

+			switch ($diff[$index][1]) {

+				// display the content on the left and right

+				case self::UNMODIFIED:

+					$leftCell = self::getCellContent(

+						$diff,

+						$indentation,

+						$separator,

+						$index,

+						self::UNMODIFIED

+					);

+					$rightCell = $leftCell;

+					break;

+

+				// display the deleted on the left and inserted content on the right

+				case self::DELETED:

+					$leftCell = self::getCellContent(

+						$diff,

+						$indentation,

+						$separator,

+						$index,

+						self::DELETED

+					);

+					$rightCell = self::getCellContent(

+							$diff,

+							$indentation,

+							$separator,

+							$index,

+							self::INSERTED

+						);

+					break;

+

+				// display the inserted content on the right

+				case self::INSERTED:

+					$leftCell = '';

+					$rightCell = self::getCellContent(

+						$diff,

+						$indentation,

+						$separator,

+						$index,

+						self::INSERTED

+					);

+					break;

+			}

+

+			// extend the HTML with the new row

+			$html .=

+				$indentation

+				. "  <tr>\n"

+				. $indentation

+				. '    <td class="diff'

+				. ($leftCell == $rightCell

+				? 'Unmodified'

+				: ($leftCell == '' ? 'Blank' : 'Deleted'))

+				. '">'

+				. $leftCell

+				. "</td>\n"

+				. $indentation

+				. '    <td class="diff'

+				. ($leftCell == $rightCell

+				? 'Unmodified'

+				: ($rightCell == '' ? 'Blank' : 'Inserted'))

+				. '">'

+				. $rightCell

+				. "</td>\n"

+				. $indentation

+				. "  </tr>\n";

+		}

+

+		// return the HTML

+		return $html.$indentation."</table>\n";

+	}

+

+	/**

+	 * Returns the content of the cell, for use in the toTable function. The

+     * parameters are:

+     *

+     * @param	string	$diff        	the diff array

+     * @param	string	$indentation 	indentation to add to every line of the generated HTML

+     * @param	string	$separator   	the separator between lines

+     * @param	string	$index       	the current index, passes by reference

+     * @param	string	$type        	the type of line

+     * @return	string					HTML string

+     */

+	private static function getCellContent($diff, $indentation, $separator, &$index, $type)

+	{

+		// initialise the HTML

+		$html = '';

+

+		// loop over the matching lines, adding them to the HTML

+		while ($index < count($diff) && $diff[$index][1] == $type) {

+			$html .=

+			'<span>'

+			. htmlspecialchars($diff[$index][0])

+				. '</span>'

+				. $separator;

+			$index++;

+		}

+

+		// return the HTML

+		return $html;

+	}

+}