--- /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 @@ -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 ''; + print ''; @@ -200,0 +193 @@ + print '
'; @@ -202,2 +195,2 @@ - print ''; - print '
'; + print ''; + print '
'; @@ -206,2 +199,2 @@ - if ($user->hasRight("ticket", "manage")) { - print ''.img_edit($langs->trans('Modify')).''; + if ($user->rights->ticket->manage) { + print ''.img_edit($langs->trans('Modify')).''; @@ -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 '
'; - //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 '
'; - /*print '
'; - print $langs->trans("More").'...'; - print '
';*/ + // 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 ' '; - print ' '; + print ' '; + print ' '; @@ -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 '
'; + if ($ret < 0) dol_print_error($this->dao->db); + + $action = GETPOST('action', 'alpha'); + @@ -271,7 +259,4 @@ - print '
'; - - if (is_array($this->dao->cache_msgs_ticket) && count($this->dao->cache_msgs_ticket) > 0) { - print '
'; - - print '
'; - print ''; + + if (is_array($this->dao->cache_msgs_ticket) && count($this->dao->cache_msgs_ticket) > 0) + { + print '
'; @@ -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 = '
'; - foreach ($documents as $doc) { - if (!empty($doc->share)) { - $isshared = 1; - $footer .= '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 .= ''; - $footer .= img_mime($filePath).' '.$doc->filename; - $footer .= ''; - - $footer .= ''; - } - } - $footer .= '
'; - if ($isshared == 1) { - print '
'; - print '
'; - print $footer; - } - } @@ -388,2 +308,0 @@ - print ''; - print ''; @@ -391 +309,0 @@ - print '
'; @@ -393 +310,0 @@ - print '
'; @@ -465 +382 @@ - print ''; - print '
'; + print '
'; --- /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 - * Copyright (C) 2020 Laurent Destailleur - * Copyright (C) 2023 Charlene Benke - * Copyright (C) 2023 Benjamin Falière - * Copyright (C) 2024 William Mead + * Copyright (C) 2019 Frédéric France @@ -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).' '.$langs->trans("Ticket").''; - $datas['picto'] .= ' '.$this->getLibStatut(4); - $datas['ref'] = '
'.$langs->trans('Ref').': '.$this->ref; - $datas['track_id'] = '
'.$langs->trans('TicketTrackId').': '.$this->track_id; - $datas['subject'] = '
'.$langs->trans('Subject').': '.$this->subject; - if ($this->date_creation) { - $datas['date_creation'] = '
'.$langs->trans('DateCreation').': '.dol_print_date($this->date_creation, 'dayhour'); - } - if ($this->date_modification) { - $datas['date_modification'] = '
'.$langs->trans('DateModification').': '.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'] = '
' . $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 = ''.$langs->trans("Ticket").''; + $label .= '
'; + $label .= ''.$langs->trans('Ref').': '.$this->ref.'
'; + $label .= ''.$langs->trans('TicketTrackId').': '.$this->track_id.'
'; + $label .= ''.$langs->trans('Subject').': '.$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').' :
'.$this->track_id.''."\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').' : '.$this->track_id.''."\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').' : '.$object->track_id.''."\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 .= '

'; - $messagePost = GETPOST('message', 'restricthtml'); - if (!dol_textishtml($messagePost)) { - $messagePost = dol_nl2br($messagePost); - } - $message .= $messagePost; - - // Customer company infos - $message .= '

'; - $message .= "=============================================="; - $message .= !empty($object->thirdparty->name) ? '
'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : ''; - $message .= !empty($object->thirdparty->town) ? '
'.$langs->trans('Town')." : ".$object->thirdparty->town : ''; - $message .= !empty($object->thirdparty->phone) ? '
'.$langs->trans('Phone')." : ".$object->thirdparty->phone : ''; - - // Email send to - $message .= '

'; - if (!empty($assigned_user_dont_have_email)) { - $message .= '
'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email; - } - foreach ($sendto as $val) { - $message .= '
'.$langs->trans('TicketNotificationRecipient').' : '.$val; - } - - // URL ticket - $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id; - $message .= '

'; - $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : '.$object->track_id.''; - @@ -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 .= '

'; - $messagePost = GETPOST('message', 'restricthtml'); - if (!dol_textishtml($messagePost)) { - $messagePost = dol_nl2br($messagePost); - } - $message .= $messagePost; - - // Data about customer - $message .= '

'; - $message .= "==============================================
"; - $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : ''; - $message .= !empty($object->thirdparty->town) ? '
'.$langs->trans('Town')." : ".$object->thirdparty->town : ''; - $message .= !empty($object->thirdparty->phone) ? '
'.$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.'
' : ''); + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient."\n" : ''); @@ -2756,12 +2576,14 @@ - $message .= '
'; - // URL ticket - $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id; - - // Add html link on url - $message .= '
'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : '.$object->track_id.'
'; - - // 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').' : '.$object->track_id.''."\n"; + + // Build final message + $message = $message_intro.$message; + + // Add signature + $message .= '
'.$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 .= '

'; - - 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.'
' : ''); - } - } - - // 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 .= '
'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : '.$object->track_id.'
'; - - // Build final message - $message = $message_intro.'

'.$message; - - // Add signature - $message .= '
'.$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 = '
'; - $return .= '
'; - $return .= ''; - $return .= img_picto('', $this->picto); - $return .= ''; - $return .= '
'; - $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).''; - if ($selected >= 0) { - $return .= ''; - } - if (!empty($arraydata['user_assignment'])) { - $return .= '
'.$arraydata['user_assignment'].''; - } - if (property_exists($this, 'type_code') && !empty($this->type_code)) { - $return .= '
'; - $return .= '
'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'
'; - } - if (method_exists($this, 'getLibStatut')) { - $return .= '
'.$this->getLibStatut(3).'
'; - } - $return .= '
'; - $return .= '
'; - $return .= '
'; - 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 '
'; - return $res; - } + $res = $this->_getNbByMonth($year, $sql); + //var_dump($res);print '
'; + 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 '
'; - return $res; - } + $res = $this->_getAmountByMonth($year, $sql); + //var_dump($res);print '
'; + 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 @@ + + * + * 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 '
' + * @return string HTML string + */ + public static function toHTML($diff, $separator = '
') + { + // 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]) + . ''; + + // 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 '
' + * @return string HTML string + */ + public static function toTable($diff, $indentation = '', $separator = '
') + { + // initialise the HTML + $html = $indentation."
\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 + . " \n" + . $indentation + . ' \n" + . $indentation + . ' \n" + . $indentation + . " \n"; + } + + // return the HTML + return $html.$indentation."
' + . $leftCell + . "' + . $rightCell + . "
\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 .= + '' + . htmlspecialchars($diff[$index][0]) + . '' + . $separator; + $index++; + } + + // return the HTML + return $html; + } +}