dolibarr  20.0.0-alpha
modTicket.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) - 2013-2018 Jean-François FERRY <hello@librethic.io>
3  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  *
18  * Module descriptor for ticket system
19  */
20 
28 require_once DOL_DOCUMENT_ROOT."/core/modules/DolibarrModules.class.php";
29 
30 
35 {
41  public function __construct($db)
42  {
43  global $langs, $conf;
44  $langs->load("ticket");
45 
46  $this->db = $db;
47 
48  // Id for module (must be unique).
49  // Use a free id here
50  // (See in Home -> System information -> Dolibarr for list of used modules id).
51  $this->numero = 56000;
52  // Key text used to identify module (for permissions, menus, etc...)
53  $this->rights_class = 'ticket';
54 
55  // Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
56  // It is used to group modules in module setup page
57  $this->family = "crm";
58  // Module position in the family
59  $this->module_position = '60';
60  // Module label (no space allowed)
61  // used if translation string 'ModuleXXXName' not found
62  // (where XXX is value of numeric property 'numero' of module)
63  $this->name = preg_replace('/^mod/i', '', get_class($this));
64  // Module description
65  // used if translation string 'ModuleXXXDesc' not found
66  // (where XXX is value of numeric property 'numero' of module)
67  $this->description = "Incident/support ticket management";
68  // Possible values for version are: 'development', 'experimental' or version
69  $this->version = 'dolibarr';
70  // Key used in llx_const table to save module status enabled/disabled
71  // (where MYMODULE is value of property name of module in uppercase)
72  $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
73  // Name of image file used for this module.
74  // If file is in theme/yourtheme/img directory under name object_pictovalue.png
75  // use this->picto='pictovalue'
76  // If file is in module/img directory under name object_pictovalue.png
77  // use this->picto='pictovalue@module'
78  $this->picto = 'ticket'; // mypicto@ticket
79  // Defined all module parts (triggers, login, substitutions, menus, css, etc...)
80  // for default path (eg: /ticket/core/xxxxx) (0=disable, 1=enable)
81  // for specific path of parts (eg: /ticket/core/modules/barcode)
82  // for specific css file (eg: /ticket/css/ticket.css.php)
83  $this->module_parts = array(
84  // Set this to 1 if module has its own trigger directory
85  'triggers' => 1,
86  );
87 
88  // Data directories to create when module is enabled.
89  // Example: this->dirs = array("/ticket/temp");
90  $this->dirs = array();
91 
92  // Config pages. Put here list of php pages
93  // stored into ticket/admin directory, used to setup module.
94  $this->config_page_url = array("ticket.php");
95 
96  // Dependencies
97  $this->hidden = false; // A condition to hide module
98  $this->depends = array('modAgenda'); // List of module class names as string that must be enabled if this module is enabled
99  $this->requiredby = array(); // List of module ids to disable if this one is disabled
100  $this->conflictwith = array(); // List of module class names as string this module is in conflict with
101  $this->phpmin = array(7, 0); // Minimum version of PHP required by module
102  $this->langfiles = array("ticket");
103 
104  // Constants
105  // List of particular constants to add when module is enabled
106  // (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive)
107  // Example:
108  $default_footer = $langs->trans('TicketMessageMailFooterText', getDolGlobalString('MAIN_INFO_SOCIETE_NOM'));
109  $this->const = array(
110  1 => array('TICKET_ENABLE_PUBLIC_INTERFACE', 'chaine', '0', 'Enable ticket public interface', 0),
111  2 => array('TICKET_ADDON', 'chaine', 'mod_ticket_simple', 'Ticket ref module', 0),
112  3 => array('TICKET_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT/doctemplates/tickets', 'Ticket templates ODT/ODS directory for templates', 0),
113  4 => array('TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND', 'chaine', 0, 'Automatically mark ticket as read when created from backend', 0),
114  5 => array('TICKET_DELAY_BEFORE_FIRST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time before a first answer to a ticket (in hours). Display a warning in tickets list if not respected.', 0),
115  6 => array('TICKET_DELAY_SINCE_LAST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time between two answers on the same ticket (in hours). Display a warning in tickets list if not respected.', 0),
116  7 => array('TICKET_NOTIFY_AT_CLOSING', 'chaine', '0', 'Default notify contacts when closing a module', 0),
117  8 => array('TICKET_PRODUCT_CATEGORY', 'chaine', 0, 'The category of product that is being used for ticket accounting', 0),
118  9 => array('TICKET_NOTIFICATION_EMAIL_FROM', 'chaine', getDolGlobalString('MAIN_MAIL_EMAIL_FROM'), 'Email to use by default as sender for messages sent from Dolibarr', 0),
119  10 => array('TICKET_MESSAGE_MAIL_INTRO', 'chaine', $langs->trans('TicketMessageMailIntroText'), 'Introduction text of ticket replies sent from Dolibarr', 0),
120  11 => array('TICKET_MESSAGE_MAIL_SIGNATURE', 'chaine', $default_footer, 'Signature to use by default for messages sent from Dolibarr', 0),
121  12 => array('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER', 'chaine', "1", 'Disable the rendering of headers in tickets', 0),
122  13 => array('MAIN_SECURITY_ENABLECAPTCHA_TICKET', 'chaine', getDolGlobalInt('MAIN_SECURITY_ENABLECAPTCHA_TICKET'), 'Enable captcha code by default', 0),
123  14 => array('TICKET_SHOW_COMPANY_LOGO', 'chaine', getDolGlobalInt('TICKET_SHOW_COMPANY_LOGO'), 'Enable logo header on ticket public page', 0),
124  15 => array('TICKET_SHOW_COMPANY_FOOTER', 'chaine', getDolGlobalInt('TICKET_SHOW_COMPANY_FOOTER'), 'Enable footer on ticket public page', 0)
125  );
126 
127 
128  $this->tabs = array(
129  'thirdparty:+ticket:Tickets:ticket:$user->rights->ticket->read:/ticket/list.php?socid=__ID__',
130  );
131 
132  // Dictionaries
133  if (!isset($conf->ticket->enabled)) {
134  $conf->ticket = new stdClass();
135  $conf->ticket->enabled = 0;
136  }
137  $this->dictionaries = array(
138  'langs' => 'ticket',
139  'tabname' => array(
140  "c_ticket_type",
141  "c_ticket_severity",
142  "c_ticket_category",
143  "c_ticket_resolution"
144  ),
145  'tablib' => array(
146  "TicketDictType",
147  "TicketDictSeverity",
148  "TicketDictCategory",
149  "TicketDictResolution"
150  ),
151  'tabsql' => array(
152  'SELECT f.rowid as rowid, f.code, f.pos, f.label, f.active, f.use_default, f.entity FROM '.MAIN_DB_PREFIX.'c_ticket_type as f WHERE f.entity IN ('.getEntity('c_ticket_type').')',
153  'SELECT f.rowid as rowid, f.code, f.pos, f.label, f.active, f.use_default, f.entity FROM '.MAIN_DB_PREFIX.'c_ticket_severity as f WHERE f.entity IN ('.getEntity('c_ticket_severity').')',
154  'SELECT f.rowid as rowid, f.code, f.pos, f.label, f.active, f.use_default, f.public, f.fk_parent, f.entity FROM '.MAIN_DB_PREFIX.'c_ticket_category as f WHERE f.entity IN ('.getEntity('c_ticket_category').')',
155  'SELECT f.rowid as rowid, f.code, f.pos, f.label, f.active, f.use_default, f.entity FROM '.MAIN_DB_PREFIX.'c_ticket_resolution as f WHERE f.entity IN ('.getEntity('c_ticket_resolution').')'
156  ),
157  'tabsqlsort' => array("pos ASC", "pos ASC", "pos ASC", "pos ASC"),
158  'tabfield' => array("code,label,pos,use_default", "code,label,pos,use_default", "code,label,pos,use_default,public,fk_parent", "code,label,pos,use_default"),
159  'tabfieldvalue' => array("code,label,pos,use_default", "code,label,pos,use_default", "code,label,pos,use_default,public,fk_parent", "code,label,pos,use_default"),
160  'tabfieldinsert' => array("code,label,pos,use_default,entity", "code,label,pos,use_default,entity", "code,label,pos,use_default,public,fk_parent,entity", "code,label,pos,use_default,entity"),
161  'tabrowid' => array("rowid", "rowid", "rowid", "rowid"),
162  'tabcond' => array(isModEnabled("ticket"), isModEnabled("ticket"), isModEnabled("ticket"), isModEnabled("ticket") && getDolGlobalString('TICKET_ENABLE_RESOLUTION')),
163  'tabhelp' => array(
164  array('code' => $langs->trans("EnterAnyCode"), 'use_default' => $langs->trans("Enter0or1")),
165  array('code' => $langs->trans("EnterAnyCode"), 'use_default' => $langs->trans("Enter0or1")),
166  array('code' => $langs->trans("EnterAnyCode"), 'use_default' => $langs->trans("Enter0or1"), 'public' => $langs->trans("Enter0or1").'<br>'.$langs->trans("TicketGroupIsPublicDesc"), 'fk_parent' => $langs->trans("IfThisCategoryIsChildOfAnother")),
167  array('code' => $langs->trans("EnterAnyCode"), 'use_default' => $langs->trans("Enter0or1"))
168  ),
169  );
170 
171  // Boxes
172  // Add here list of php file(s) stored in core/boxes that contains class to show a box.
173  $this->boxes = array(
174  0 => array('file' => 'box_last_ticket.php', 'enabledbydefaulton' => 'Home'),
175  1 => array('file' => 'box_last_modified_ticket.php', 'enabledbydefaulton' => 'Home'),
176  2 => array('file' => 'box_ticket_by_severity.php', 'enabledbydefaulton' => 'ticketindex'),
177  3 => array('file' => 'box_graph_nb_ticket_last_x_days.php', 'enabledbydefaulton' => 'ticketindex'),
178  4 => array('file' => 'box_graph_nb_tickets_type.php', 'enabledbydefaulton' => 'ticketindex'),
179  5 => array('file' => 'box_new_vs_close_ticket.php', 'enabledbydefaulton' => 'ticketindex')
180  ); // Boxes list
181 
182  // Permissions
183  $this->rights = array(); // Permission array used by this module
184 
185  $r = 0;
186  $this->rights[$r][0] = 56001; // id de la permission
187  $this->rights[$r][1] = "Read ticket"; // libelle de la permission
188  $this->rights[$r][2] = 'r'; // type de la permission (deprecated)
189  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
190  $this->rights[$r][4] = 'read';
191 
192  $r++;
193  $this->rights[$r][0] = 56002; // id de la permission
194  $this->rights[$r][1] = "Create les tickets"; // libelle de la permission
195  $this->rights[$r][2] = 'w'; // type de la permission (deprecated)
196  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
197  $this->rights[$r][4] = 'write';
198 
199  $r++;
200  $this->rights[$r][0] = 56003; // id de la permission
201  $this->rights[$r][1] = "Delete les tickets"; // libelle de la permission
202  $this->rights[$r][2] = 'd'; // type de la permission (deprecated)
203  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
204  $this->rights[$r][4] = 'delete';
205 
206  $r++;
207  $this->rights[$r][0] = 56004; // id de la permission
208  $this->rights[$r][1] = "Manage tickets"; // libelle de la permission
209  //$this->rights[$r][2] = 'd'; // type de la permission (deprecated)
210  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
211  $this->rights[$r][4] = 'manage';
212 
213  $r++;
214  $this->rights[$r][0] = 56006; // id de la permission
215  $this->rights[$r][1] = "Export ticket"; // libelle de la permission
216  //$this->rights[$r][2] = 'd'; // type de la permission (deprecated)
217  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
218  $this->rights[$r][4] = 'export';
219 
220  /* Seems not used and in conflict with societe->client->voir (see all thirdparties)
221  $r++;
222  $this->rights[$r][0] = 56005; // id de la permission
223  $this->rights[$r][1] = 'See all tickets, even if not assigned to (not effective for external users, always restricted to the thirdpardy they depends on)'; // libelle de la permission
224  $this->rights[$r][2] = 'r'; // type de la permission (deprecated)
225  $this->rights[$r][3] = 0; // La permission est-elle une permission par default
226  $this->rights[$r][4] = 'view';
227  $this->rights[$r][5] = 'all';
228  */
229 
230  // Main menu entries
231  $this->menu = array(); // List of menus to add
232  $r = 0;
233 
234  /*$this->menu[$r] = array('fk_menu' => 0, // Put 0 if this is a top menu
235  'type' => 'top', // This is a Top menu entry
236  'titre' => 'Ticket',
237  'prefix' => img_picto('', $this->picto, 'class="paddingright pictofixedwidth em092"'),
238  'mainmenu' => 'ticket',
239  'leftmenu' => '1', // Use 1 if you also want to add left menu entries using this descriptor.
240  'url' => '/ticket/index.php',
241  'langs' => 'ticket', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
242  'position' => 88,
243  'enabled' => 'isModEnabled("ticket")',
244  'perms' => '$user->rights->ticket->read', // Use 'perms'=>'$user->rights->ticket->level1->level2' if you want your menu with a permission rules
245  'target' => '',
246  'user' => 2); // 0=Menu for internal users, 1=external users, 2=both
247  $r++;*/
248 
249  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket',
250  'type' => 'left',
251  'titre' => 'Ticket',
252  'prefix' => img_picto('', $this->picto, 'class="paddingright pictofixedwidth em092"'),
253  'mainmenu' => 'ticket',
254  'leftmenu' => 'ticket',
255  'url' => '/ticket/index.php',
256  'langs' => 'ticket',
257  'position' => 101,
258  'enabled' => 'isModEnabled("ticket")',
259  'perms' => '$user->rights->ticket->read',
260  'target' => '',
261  'user' => 2);
262  $r++;
263 
264  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
265  'type' => 'left',
266  'titre' => 'NewTicket',
267  'mainmenu' => 'ticket',
268  'url' => '/ticket/card.php?action=create',
269  'langs' => 'ticket',
270  'position' => 102,
271  'enabled' => 'isModEnabled("ticket")',
272  'perms' => '$user->rights->ticket->write',
273  'target' => '',
274  'user' => 2);
275  $r++;
276 
277  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
278  'type' => 'left',
279  'titre' => 'List',
280  'mainmenu' => 'ticket',
281  'leftmenu' => 'ticketlist',
282  'url' => '/ticket/list.php?search_fk_status=non_closed',
283  'langs' => 'ticket',
284  'position' => 103,
285  'enabled' => 'isModEnabled("ticket")',
286  'perms' => '$user->rights->ticket->read',
287  'target' => '',
288  'user' => 2);
289  $r++;
290 
291  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
292  'type' => 'left',
293  'titre' => 'MenuTicketMyAssign',
294  'mainmenu' => 'ticket',
295  'leftmenu' => 'ticketmy',
296  'url' => '/ticket/list.php?mode=mine&search_fk_status=non_closed',
297  'langs' => 'ticket',
298  'position' => 105,
299  'enabled' => 'isModEnabled("ticket")',
300  'perms' => '$user->rights->ticket->read',
301  'target' => '',
302  'user' => 0);
303  $r++;
304 
305  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
306  'type' => 'left',
307  'titre' => 'Statistics',
308  'mainmenu' => 'ticket',
309  'url' => '/ticket/stats/index.php',
310  'langs' => 'ticket',
311  'position' => 107,
312  'enabled' => 'isModEnabled("ticket")',
313  'perms' => '$user->rights->ticket->read',
314  'target' => '',
315  'user' => 0);
316  $r++;
317 
318  $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
319  'type' => 'left',
320  'titre' => 'Categories',
321  'mainmenu' => 'ticket',
322  'url' => '/categories/index.php?type=12',
323  'langs' => 'ticket',
324  'position' => 107,
325  'enabled' => '$conf->categorie->enabled',
326  'perms' => '$user->rights->ticket->read',
327  'target' => '',
328  'user' => 0);
329  $r++;
330 
331  // Exports
332  //--------
333  $r = 1;
334 
335  // Export list of tickets and attributes
336  $langs->load("ticket");
337  $this->export_code[$r] = $this->rights_class.'_'.$r;
338  $this->export_label[$r] = 'ExportDataset_ticket_1'; // Translation key (used only if key ExportDataset_xxx_z not found)
339  $this->export_permission[$r] = array(array("ticket", "export"));
340  $this->export_icon[$r] = 'ticket';
341  $keyforclass = 'Ticket';
342  $keyforclassfile = '/ticket/class/ticket.class.php';
343  $keyforelement = 'ticket';
344  include DOL_DOCUMENT_ROOT.'/core/commonfieldsinexport.inc.php';
345  $keyforselect = 'ticket';
346  $keyforaliasextra = 'extra';
347  $keyforelement = 'ticket'; // @phan-suppress-current-line PhanPluginRedundantAssignment
348  include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
349  $this->export_sql_start[$r] = 'SELECT DISTINCT ';
350  $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'ticket as t';
351  $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'ticket_extrafields as extra on (t.rowid = extra.fk_object)';
352  $this->export_sql_end[$r] .= ' WHERE 1 = 1';
353  $this->export_sql_end[$r] .= ' AND t.entity IN ('.getEntity('ticket').')';
354  $r++;
355  }
356 
365  public function init($options = '')
366  {
367  global $conf, $langs;
368 
369  $result = $this->_load_tables('/install/mysql/', 'ticket');
370  if ($result < 0) {
371  return -1; // Do not activate module if error 'not allowed' returned when loading module SQL queries (the _load_table run sql with run_sql with the error allowed parameter set to 'default')
372  }
373 
374  // Permissions
375  $this->remove($options);
376 
377  //ODT template
378  $src = DOL_DOCUMENT_ROOT.'/install/doctemplates/tickets/template_ticket.odt';
379  $dirodt = DOL_DATA_ROOT.'/doctemplates/tickets';
380  $dest = $dirodt.'/template_ticket.odt';
381 
382  if (file_exists($src) && !file_exists($dest)) {
383  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
384  dol_mkdir($dirodt);
385  $result = dol_copy($src, $dest, 0, 0);
386  if ($result < 0) {
387  $langs->load("errors");
388  $this->error = $langs->trans('ErrorFailToCopyFile', $src, $dest);
389  return 0;
390  }
391  }
392 
393  $sql = array(
394  array("sql" => "insert into llx_c_type_contact(rowid, element, source, code, libelle, active ) values (110120, 'ticket', 'internal', 'SUPPORTTEC', 'Utilisateur assigné au ticket', 1);", "ignoreerror" => 1),
395  array("sql" => "insert into llx_c_type_contact(rowid, element, source, code, libelle, active ) values (110121, 'ticket', 'internal', 'CONTRIBUTOR', 'Intervenant', 1);", "ignoreerror" => 1),
396  array("sql" => "insert into llx_c_type_contact(rowid, element, source, code, libelle, active ) values (110122, 'ticket', 'external', 'SUPPORTCLI', 'Contact client suivi incident', 1);", "ignoreerror" => 1),
397  array("sql" => "insert into llx_c_type_contact(rowid, element, source, code, libelle, active ) values (110123, 'ticket', 'external', 'CONTRIBUTOR', 'Intervenant', 1);", "ignoreerror" => 1),
398  // remove old settings
399  "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = 'TICKET_ADDON_PDF_ODT_PATH' AND type = 'ticket' AND entity = ".((int) $conf->entity),
400  // activate default odt templates
401  array("sql" => "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, libelle, entity, description) VALUES('generic_ticket_odt','ticket','ODT templates',".((int) $conf->entity).",'TICKET_ADDON_PDF_ODT_PATH');", "ignoreerror" => 1),
402  );
403 
404  return $this->_init($sql, $options);
405  }
406 }
Class DolibarrModules.
_init($array_sql, $options='')
Enables a module.
_load_tables($reldir, $onlywithsuffix='')
Create tables and keys required by module:
Description and activation class for module Ticket.
init($options='')
Function called when module is enabled.
__construct($db)
Constructor.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
Definition: files.lib.php:755
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:125