From 079608fe00c1296787ba57fb7734c7fdee5f33af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20David?= Date: Fri, 2 Jul 2021 10:26:03 +0200 Subject: [PATCH 1/6] NEW #18046 Add ticket ticket categories functions --- htdocs/categories/class/categorie.class.php | 13 +- htdocs/core/class/html.form.class.php | 219 ++++++++++++++++++++ htdocs/core/class/html.formticket.class.php | 9 + htdocs/core/modules/modTicket.class.php | 13 ++ htdocs/ticket/class/ticket.class.php | 45 ++++ 5 files changed, 296 insertions(+), 3 deletions(-) diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index b98cff5b3a5..caeab5b8ddd 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -34,6 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php'; require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; @@ -56,6 +57,7 @@ class Categorie extends CommonObject const TYPE_WAREHOUSE = 'warehouse'; const TYPE_ACTIONCOMM = 'actioncomm'; const TYPE_WEBSITE_PAGE = 'website_page'; + const TYPE_TICKET = 'ticket'; /** * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png @@ -78,7 +80,8 @@ class Categorie extends CommonObject 'bank_line' => 8, 'warehouse' => 9, 'actioncomm' => 10, - 'website_page' => 11 + 'website_page' => 11, + 'ticket' => 12 ); /** @@ -98,7 +101,8 @@ class Categorie extends CommonObject 8 => 'bank_line', 9 => 'warehouse', 10 => 'actioncomm', - 11 => 'website_page' + 11 => 'website_page', + 12 => 'ticket' ); /** @@ -141,7 +145,8 @@ class Categorie extends CommonObject 'project' => 'Project', 'warehouse'=> 'Entrepot', 'actioncomm' => 'ActionComm', - 'website_page' => 'WebsitePage' + 'website_page' => 'WebsitePage', + 'ticket' => 'Ticket' ); /** @@ -234,6 +239,8 @@ class Categorie extends CommonObject * @see Categorie::TYPE_WAREHOUSE * @see Categorie::TYPE_ACTIONCOMM * @see Categorie::TYPE_WEBSITE_PAGE + * @see Categorie::TYPE_TICKET + */ public $type; diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 0b1504d229a..2d648ef1fd9 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -6578,6 +6578,225 @@ class Form return; } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of tickets in Ajax if Ajax activated or go to select_tickets_list + * + * @param int $selected Preselected tickets + * @param string $htmlname Name of HTML select field (must be unique in page). + * @param int $limit Limit on number of returned lines + * @param int $status Ticket status + * @param string $selected_input_value Value of preselected input text (for use with ajax) + * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...]) + * @param string $nooutput No print, return the output into a string + * @return void|string + */ + public function select_tickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0) + { + // phpcs:enable + global $langs, $conf; + + $out = ''; + + // check parameters + if (is_null($ajaxoptions)) $ajaxoptions = array(); + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) + { + $placeholder = ''; + + if ($selected && empty($selected_input_value)) + { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + $tickettmpselect = new Ticket($this->db); + $tickettmpselect->fetch($selected); + $selected_input_value = $tickettmpselect->ref; + unset($tickettmpselect); + } + + $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions); + + if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } + $out .= 'global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } else { + $out .= $this->select_tickets_list($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss); + } + + if (empty($nooutput)) print $out; + else return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of tickets. + * Called by select_tickets. + * + * @param int $selected Preselected ticket + * @param string $htmlname Name of select html + * @param string $filtertype Filter on ticket type + * @param int $limit Limit on number of returned lines + * @param string $filterkey Filter on product + * @param int $status Ticket status + * @param int $outputmode 0=HTML select string, 1=Array + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @return array Array of keys for json + */ + public function select_tickets_list($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf, $user, $db; + + $out = ''; + $outarray = array(); + + $selectFields = " p.rowid, p.ref, p.message"; + + $sql = "SELECT "; + $sql .= $selectFields; + $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p"; + $sql .= ' WHERE p.entity IN ('.getEntity('ticket').')'; + + // Add criteria on ref/label + if ($filterkey != '') + { + $sql .= ' AND ('; + $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) + { + if ($i > 0) $sql .= " AND "; + $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'"; + $sql .= ")"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + $sql .= ')'; + } + + $sql .= $this->db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::select_tickets_list search tickets", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; + + $num = $this->db->num_rows($result); + + $events = null; + + if (!$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT); + } + + $out .= ''; + + $this->db->free($result); + + if (empty($outputmode)) return $out; + return $outarray; + } else { + dol_print_error($db); + } + } + + /** + * constructTicketListOption. + * This define value for &$opt and &$optJson. + * + * @param resource $objp Result set of fetch + * @param string $opt Option (var used for returned value in string option format) + * @param string $optJson Option (var used for returned value in json format) + * @param string $selected Preselected value + * @param string $filterkey Filter key to highlight + * @return void + */ + protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '') + { + global $langs, $conf, $user, $db; + + $outkey = ''; + $outval = ''; + $outref = ''; + $outlabel = ''; + $outtype = ''; + + $label = $objp->label; + + $outkey = $objp->rowid; + $outref = $objp->ref; + $outlabel = $objp->label; + $outtype = $objp->fk_product_type; + + $opt = '\n"; + $optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtypem); + } + /** * Generic method to select a component from a combo list. diff --git a/htdocs/core/class/html.formticket.class.php b/htdocs/core/class/html.formticket.class.php index daafa88333f..2178a02cb89 100644 --- a/htdocs/core/class/html.formticket.class.php +++ b/htdocs/core/class/html.formticket.class.php @@ -272,6 +272,15 @@ class FormTicket print ''; } + //Categories + if ($conf->categorie->enabled) { + // Categories + print ''.$langs->trans("Categories").''; + $cate_arbo = $form->select_all_categories(Categorie::TYPE_TICKET, '', 'parent', 64, 0, 1); + print img_picto('', 'category').$form->multiselectarray('categories', $cate_arbo, GETPOST('categories', 'array'), '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0); + print ""; + } + // Attached files if (!empty($this->withfile)) { // Define list of attached files diff --git a/htdocs/core/modules/modTicket.class.php b/htdocs/core/modules/modTicket.class.php index 8b1a1dc8525..2a717ae4f42 100644 --- a/htdocs/core/modules/modTicket.class.php +++ b/htdocs/core/modules/modTicket.class.php @@ -283,6 +283,19 @@ class modTicket extends DolibarrModules 'target' => '', 'user' => 0); $r++; + + $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket', + 'type' => 'left', + 'titre' => 'Categories', + 'mainmenu' => 'ticket', + 'url' => '/categories/index.php?type=12', + 'langs' => 'ticket', + 'position' => 107, + 'enabled' => '$conf->categorie->enabled', + 'perms' => '$user->rights->ticket->read', + 'target' => '', + 'user' => 0); + $r++; } /** diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 89f239db6c6..36d90c5519d 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -2497,6 +2497,51 @@ class Ticket extends CommonObject return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype); } + /** + * 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 void + */ + 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; + } /** * Add new message on a ticket (private/public area). Can also send it be email if GETPOST('send_email', 'int') is set. From 642d84a844db1666e6d8102f897aae43ef2100cd Mon Sep 17 00:00:00 2001 From: David Date: Mon, 12 Jul 2021 09:26:18 +0200 Subject: [PATCH 2/6] fix: code syntax --- htdocs/core/class/html.form.class.php | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index a569785863c..5ef8d804a6b 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -6588,6 +6588,7 @@ class Form * * @param int $selected Preselected tickets * @param string $htmlname Name of HTML select field (must be unique in page). + * @param string $filtertype To add a filter * @param int $limit Limit on number of returned lines * @param int $status Ticket status * @param string $selected_input_value Value of preselected input text (for use with ajax) @@ -6611,12 +6612,10 @@ class Form // check parameters if (is_null($ajaxoptions)) $ajaxoptions = array(); - if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) - { + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) { $placeholder = ''; - if ($selected && empty($selected_input_value)) - { + if ($selected && empty($selected_input_value)) { require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; $tickettmpselect = new Ticket($this->db); $tickettmpselect->fetch($selected); @@ -6679,16 +6678,14 @@ class Form $sql .= ' WHERE p.entity IN ('.getEntity('ticket').')'; // Add criteria on ref/label - if ($filterkey != '') - { + if ($filterkey != '') { $sql .= ' AND ('; $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on // For natural search $scrit = explode(' ', $filterkey); $i = 0; if (count($scrit) > 1) $sql .= "("; - foreach ($scrit as $crit) - { + foreach ($scrit as $crit) { if ($i > 0) $sql .= " AND "; $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'"; $sql .= ")"; @@ -6703,8 +6700,7 @@ class Form // Build output string dol_syslog(get_class($this)."::select_tickets_list search tickets", LOG_DEBUG); $result = $this->db->query($sql); - if ($result) - { + if ($result) { require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; @@ -6712,8 +6708,7 @@ class Form $events = null; - if (!$forcecombo) - { + if (!$forcecombo) { include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; $out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT); } @@ -6723,8 +6718,7 @@ class Form $textifempty = ''; // Do not use textifempty = ' ' or ' ' here, or search on key will search on ' key'. //if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty=''; - if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) - { + if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) { if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty); else $textifempty .= $langs->trans("All"); } else { @@ -6733,8 +6727,7 @@ class Form if ($showempty) $out .= ''; $i = 0; - while ($num && $i < $num) - { + while ($num && $i < $num) { $opt = ''; $optJson = array(); $objp = $this->db->fetch_object($result); From b59c1f1a3487fb334c824802b69c5a6d5736752f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20David?= Date: Mon, 12 Jul 2021 14:17:30 +0200 Subject: [PATCH 3/6] fix: rename select_tickets into selectTickets --- htdocs/core/class/html.form.class.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 5ef8d804a6b..23a6b724d14 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -6582,7 +6582,6 @@ class Form return; } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return list of tickets in Ajax if Ajax activated or go to select_tickets_list * @@ -6602,7 +6601,7 @@ class Form * @param string $nooutput No print, return the output into a string * @return void|string */ - public function select_tickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0) + public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0) { // phpcs:enable global $langs, $conf; @@ -6648,7 +6647,7 @@ class Form // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return list of tickets. - * Called by select_tickets. + * Called by selectTickets. * * @param int $selected Preselected ticket * @param string $htmlname Name of select html From 12e327d6b4055711ef667999059d0aa27f02809e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20David?= Date: Mon, 12 Jul 2021 14:37:07 +0200 Subject: [PATCH 4/6] fix: remove phpcs:enable tag --- htdocs/core/class/html.form.class.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 23a6b724d14..cbb1f09f3d8 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -6663,7 +6663,6 @@ class Form */ public function select_tickets_list($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '') { - // phpcs:enable global $langs, $conf, $user, $db; $out = ''; @@ -6738,7 +6737,6 @@ class Form $out .= $opt; array_push($outarray, $optJson); - $i++; } From bbce0f9c1bd89fda1c3774599d141d2a6e4a2bfc Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 13 Jul 2021 13:01:34 +0200 Subject: [PATCH 5/6] Update html.form.class.php --- htdocs/core/class/html.form.class.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index cbb1f09f3d8..8c588cfb2e4 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -4454,7 +4454,6 @@ class Form */ public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '') { - // phpcs:enable global $langs, $conf; $langs->load("admin"); @@ -6603,7 +6602,6 @@ class Form */ public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0) { - // phpcs:enable global $langs, $conf; $out = ''; From 66f38d001265fa8441fdd7c51ca6f4fbbc5fb495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20David?= Date: Tue, 13 Jul 2021 14:00:16 +0200 Subject: [PATCH 6/6] fix: rename select_tickets_list into selectTicketsList --- htdocs/core/class/html.form.class.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 8c588cfb2e4..482b1672d51 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -6582,7 +6582,7 @@ class Form } /** - * Return list of tickets in Ajax if Ajax activated or go to select_tickets_list + * Return list of tickets in Ajax if Ajax activated or go to selectTicketsList * * @param int $selected Preselected tickets * @param string $htmlname Name of HTML select field (must be unique in page). @@ -6634,7 +6634,7 @@ class Form $out .= img_picto($langs->trans("Search"), 'search'); } } else { - $out .= $this->select_tickets_list($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss); + $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss); } if (empty($nooutput)) print $out; @@ -6642,7 +6642,6 @@ class Form } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return list of tickets. * Called by selectTickets. @@ -6659,7 +6658,7 @@ class Form * @param string $morecss Add more css on select * @return array Array of keys for json */ - public function select_tickets_list($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '') + public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '') { global $langs, $conf, $user, $db; @@ -6694,7 +6693,7 @@ class Form $sql .= $this->db->plimit($limit, 0); // Build output string - dol_syslog(get_class($this)."::select_tickets_list search tickets", LOG_DEBUG); + dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG); $result = $this->db->query($sql); if ($result) { require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';