list.php 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. <?php
  2. /* Copyright (C) 2013-2018 Jean-François FERRY <hello@librethic.io>
  3. * Copyright (C) 2016 Christophe Battarel <christophe@altairis.fr>
  4. * Copyright (C) 2018 Regis Houssin <regis.houssin@inodbox.com>
  5. * Copyright (C) 2019-2021 Juanjo Menent <jmenent@2byte.es>
  6. * Copyright (C) 2019-2020 Laurent Destailleur <eldy@users.sourceforge.net>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. /**
  22. * \file htdocs/ticket/list.php
  23. * \ingroup ticket
  24. * \brief List page for tickets
  25. */
  26. // Load Dolibarr environment
  27. require '../main.inc.php';
  28. require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php';
  29. require_once DOL_DOCUMENT_ROOT.'/core/class/html.formticket.class.php';
  30. require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
  31. require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
  32. require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
  33. include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  34. include_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
  35. include_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
  36. // Load translation files required by the page
  37. $langs->loadLangs(array("ticket", "companies", "other", "projects"));
  38. // Get parameters
  39. $action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
  40. $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
  41. $show_files = GETPOST('show_files', 'int'); // Show files area generated by bulk actions ?
  42. $confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation
  43. $cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button
  44. $toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list
  45. $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'ticketlist'; // To manage different context of search
  46. $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
  47. $optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
  48. $mode = GETPOST('mode', 'alpha');
  49. $id = GETPOST('id', 'int');
  50. $msg_id = GETPOST('msg_id', 'int');
  51. $socid = GETPOST('socid', 'int');
  52. $projectid = GETPOST('projectid', 'int');
  53. $project_ref = GETPOST('project_ref', 'alpha');
  54. $search_societe = GETPOST('search_societe', 'alpha');
  55. $search_fk_project = GETPOST('search_fk_project', 'int') ?GETPOST('search_fk_project', 'int') : GETPOST('projectid', 'int');
  56. $search_date_start = dol_mktime(0, 0, 0, GETPOST('search_date_startmonth', 'int'), GETPOST('search_date_startday', 'int'), GETPOST('search_date_startyear', 'int'));
  57. $search_date_end = dol_mktime(23, 59, 59, GETPOST('search_date_endmonth', 'int'), GETPOST('search_date_endday', 'int'), GETPOST('search_date_endyear', 'int'));
  58. $search_dateread_start = dol_mktime(0, 0, 0, GETPOST('search_dateread_startmonth', 'int'), GETPOST('search_dateread_startday', 'int'), GETPOST('search_dateread_startyear', 'int'));
  59. $search_dateread_end = dol_mktime(23, 59, 59, GETPOST('search_dateread_endmonth', 'int'), GETPOST('search_dateread_endday', 'int'), GETPOST('search_dateread_endyear', 'int'));
  60. $search_dateclose_start = dol_mktime(0, 0, 0, GETPOST('search_dateclose_startmonth', 'int'), GETPOST('search_dateclose_startday', 'int'), GETPOST('search_dateclose_startyear', 'int'));
  61. $search_dateclose_end = dol_mktime(23, 59, 59, GETPOST('search_dateclose_endmonth', 'int'), GETPOST('search_dateclose_endday', 'int'), GETPOST('search_dateclose_endyear', 'int'));
  62. // Load variable for pagination
  63. $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
  64. $sortfield = GETPOST('sortfield', 'aZ09comma');
  65. $sortorder = GETPOST('sortorder', 'aZ09comma');
  66. $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
  67. if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
  68. // If $page is not defined, or '' or -1 or if we click on clear filters
  69. $page = 0;
  70. }
  71. $offset = $limit * $page;
  72. $pageprev = $page - 1;
  73. $pagenext = $page + 1;
  74. // Initialize technical objects
  75. $object = new Ticket($db);
  76. $extrafields = new ExtraFields($db);
  77. $diroutputmassaction = $conf->ticket->dir_output.'/temp/massgeneration/'.$user->id;
  78. if ($socid > 0) {
  79. $hookmanager->initHooks(array('thirdpartyticket'));
  80. } elseif ($projectid > 0) {
  81. $hookmanager->initHooks(array('projectticket'));
  82. } else {
  83. $hookmanager->initHooks(array('ticketlist'));
  84. }
  85. // Fetch optionals attributes and labels
  86. $extrafields->fetch_name_optionals_label($object->table_element);
  87. $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
  88. // Default sort order (if not yet defined by previous GETPOST)
  89. if (!$sortfield) {
  90. $sortfield = "t.datec";
  91. }
  92. if (!$sortorder) {
  93. $sortorder = "DESC";
  94. }
  95. if (GETPOST('search_fk_status', 'alpha') == 'non_closed') {
  96. $_GET['search_fk_statut'][] = 'openall'; // For backward compatibility
  97. }
  98. // Initialize array of search criterias
  99. $search_all = (GETPOSTISSET("search_all") ? GETPOST("search_all", 'alpha') : GETPOST('sall'));
  100. $search = array();
  101. foreach ($object->fields as $key => $val) {
  102. if (GETPOST('search_'.$key, 'alpha') !== '') {
  103. $search[$key] = GETPOST('search_'.$key, 'alpha');
  104. }
  105. if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
  106. $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOST('search_'.$key.'_dtstartmonth', 'int'), GETPOST('search_'.$key.'_dtstartday', 'int'), GETPOST('search_'.$key.'_dtstartyear', 'int'));
  107. $search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOST('search_'.$key.'_dtendmonth', 'int'), GETPOST('search_'.$key.'_dtendday', 'int'), GETPOST('search_'.$key.'_dtendyear', 'int'));
  108. }
  109. }
  110. // List of fields to search into when doing a "search in all"
  111. $fieldstosearchall = array();
  112. foreach ($object->fields as $key => $val) {
  113. if (!empty($val['searchall'])) {
  114. $fieldstosearchall['t.'.$key] = $val['label'];
  115. }
  116. }
  117. $fieldstosearchall['s.name_alias'] = "AliasNameShort";
  118. $fieldstosearchall['s.zip'] = "Zip";
  119. $fieldstosearchall['s.town'] = "Town";
  120. // Definition of array of fields for columns
  121. $arrayfields = array();
  122. foreach ($object->fields as $key => $val) {
  123. // If $val['visible']==0, then we never show the field
  124. if (!empty($val['visible'])) {
  125. $visible = (int) dol_eval($val['visible'], 1);
  126. $arrayfields['t.'.$key] = array(
  127. 'label'=>$val['label'],
  128. 'checked'=>(($visible < 0) ? 0 : 1),
  129. 'enabled'=>(abs($visible) != 3 && dol_eval($val['enabled'], 1)),
  130. 'position'=>$val['position'],
  131. 'help'=> isset($val['help']) ? $val['help'] : ''
  132. );
  133. }
  134. }
  135. // Extra fields
  136. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php';
  137. $object->fields = dol_sort_array($object->fields, 'position');
  138. $arrayfields = dol_sort_array($arrayfields, 'position');
  139. // Security check
  140. if (!$user->rights->ticket->read) {
  141. accessforbidden();
  142. }
  143. // restrict view to current user's company
  144. if ($user->socid > 0) $socid = $user->socid;
  145. // Store current page url
  146. $url_page_current = DOL_URL_ROOT.'/ticket/list.php';
  147. if ($project_ref) {
  148. $tmpproject = new Project($db);
  149. $tmpproject->fetch(0, $project_ref);
  150. $projectid = $tmpproject->id;
  151. $search_fk_project = $projectid;
  152. }
  153. $permissiontoread = $user->rights->ticket->read;
  154. $permissiontoadd = $user->rights->ticket->write;
  155. $permissiontodelete = $user->rights->ticket->delete;
  156. $error = 0;
  157. /*
  158. * Actions
  159. */
  160. if (GETPOST('cancel', 'alpha')) {
  161. $action = 'list';
  162. $massaction = '';
  163. }
  164. if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend' && $massaction != 'presendonclose' && $massaction != 'close') {
  165. $massaction = '';
  166. }
  167. $parameters = array();
  168. if ($socid > 0) {
  169. $parameters['socid'] = $socid;
  170. }
  171. if ($projectid > 0) {
  172. $parameters['projectid'] = $projectid;
  173. }
  174. $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
  175. if ($reshook < 0) {
  176. setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
  177. }
  178. if (empty($reshook)) {
  179. // Selection of new fields
  180. include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
  181. // Purge search criteria
  182. if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
  183. foreach ($object->fields as $key => $val) {
  184. $search[$key] = '';
  185. if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
  186. $search[$key.'_dtstart'] = '';
  187. $search[$key.'_dtend'] = '';
  188. }
  189. }
  190. $toselect = array();
  191. $search_array_options = array();
  192. $search_date_start = '';
  193. $search_date_end = '';
  194. $search_dateread_start = '';
  195. $search_dateread_end = '';
  196. $search_dateclose_start = '';
  197. $search_dateclose_end = '';
  198. }
  199. if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')
  200. || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) {
  201. $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation
  202. }
  203. // Mass actions
  204. $objectclass = 'Ticket';
  205. $objectlabel = 'Ticket';
  206. $uploaddir = $conf->ticket->dir_output;
  207. include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
  208. // Close records
  209. if (!$error && $massaction == 'close' && $permissiontoadd) {
  210. $objecttmp = new $objectclass($db);
  211. if (!$error) {
  212. $db->begin();
  213. $nbok = 0;
  214. foreach ($toselect as $toselectid) {
  215. $result = $objecttmp->fetch($toselectid);
  216. if ($result > 0) {
  217. $result = $objecttmp->close($user);
  218. if ($result < 0) {
  219. setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
  220. $error++;
  221. break;
  222. } else {
  223. $nbok++;
  224. }
  225. } else {
  226. setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
  227. $error++;
  228. break;
  229. }
  230. }
  231. if (!$error) {
  232. if ($nbok > 1) {
  233. setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
  234. } else {
  235. setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
  236. }
  237. $db->commit();
  238. } else {
  239. $db->rollback();
  240. }
  241. //var_dump($listofobjectthirdparties);exit;
  242. }
  243. }
  244. // Reopen records
  245. if (!$error && $massaction == 'reopen' && $permissiontoadd) {
  246. $objecttmp = new $objectclass($db);
  247. if (!$error) {
  248. $db->begin();
  249. $nbok = 0;
  250. foreach ($toselect as $toselectid) {
  251. $result = $objecttmp->fetch($toselectid);
  252. if ($result > 0) {
  253. if ($objecttmp->status == Ticket::STATUS_CLOSED || $objecttmp->status == Ticket::STATUS_CANCELED) {
  254. $result = $objecttmp->setStatut(Ticket::STATUS_ASSIGNED);
  255. if ($result < 0) {
  256. setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
  257. $error++;
  258. break;
  259. } else {
  260. $nbok++;
  261. }
  262. } else {
  263. $langs->load("errors");
  264. setEventMessages($langs->trans("ErrorObjectMustHaveStatusClosedToBeReOpened", $objecttmp->ref), null, 'errors');
  265. $error++;
  266. break;
  267. }
  268. } else {
  269. setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
  270. $error++;
  271. break;
  272. }
  273. }
  274. if (!$error) {
  275. if ($nbok > 1) {
  276. setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
  277. } else {
  278. setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
  279. }
  280. $db->commit();
  281. } else {
  282. $db->rollback();
  283. }
  284. //var_dump($listofobjectthirdparties);exit;
  285. }
  286. }
  287. }
  288. /*
  289. * View
  290. */
  291. $form = new Form($db);
  292. $formTicket = new FormTicket($db);
  293. $now = dol_now();
  294. $user_temp = new User($db);
  295. $socstatic = new Societe($db);
  296. $help_url = '';
  297. $title = $langs->trans('Tickets');
  298. $morejs = array();
  299. $morecss = array();
  300. // Build and execute select
  301. // --------------------------------------------------------------------
  302. $sql = 'SELECT ';
  303. $sql .= $object->getFieldList('t');
  304. // Add fields from extrafields
  305. if (!empty($extrafields->attributes[$object->table_element]['label'])) {
  306. foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
  307. $sql .= ($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? ", ef.".$key." as options_".$key : '');
  308. }
  309. }
  310. // Add fields from hooks
  311. $parameters = array();
  312. $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  313. $sql .= $hookmanager->resPrint;
  314. $sql = preg_replace('/,\s*$/', '', $sql);
  315. $sqlfields = $sql; // $sql fields to remove for count total
  316. $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
  317. if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) {
  318. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)";
  319. }
  320. // Add table from hooks
  321. $parameters = array();
  322. $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook
  323. $sql .= $hookmanager->resPrint;
  324. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
  325. $sql .= " WHERE t.entity IN (".getEntity($object->element).")";
  326. if ($socid > 0) {
  327. $sql .= " AND t.fk_soc = ".((int) $socid);
  328. }
  329. foreach ($search as $key => $val) {
  330. if ($key == 'fk_statut' && !empty($search['fk_statut'])) {
  331. $newarrayofstatus = array();
  332. foreach ($search['fk_statut'] as $key2 => $val2) {
  333. if (in_array($val2, array('openall', 'closeall'))) {
  334. continue;
  335. }
  336. $newarrayofstatus[] = $val2;
  337. }
  338. if ($search['fk_statut'] == 'openall' || in_array('openall', $search['fk_statut'])) {
  339. $newarrayofstatus[] = Ticket::STATUS_NOT_READ;
  340. $newarrayofstatus[] = Ticket::STATUS_READ;
  341. $newarrayofstatus[] = Ticket::STATUS_ASSIGNED;
  342. $newarrayofstatus[] = Ticket::STATUS_IN_PROGRESS;
  343. $newarrayofstatus[] = Ticket::STATUS_NEED_MORE_INFO;
  344. $newarrayofstatus[] = Ticket::STATUS_WAITING;
  345. }
  346. if ($search['fk_statut'] == 'closeall' || in_array('closeall', $search['fk_statut'])) {
  347. $newarrayofstatus[] = Ticket::STATUS_CLOSED;
  348. $newarrayofstatus[] = Ticket::STATUS_CANCELED;
  349. }
  350. if (count($newarrayofstatus)) {
  351. $sql .= natural_search($key, join(',', $newarrayofstatus), 2);
  352. }
  353. continue;
  354. } elseif ($key == 'fk_user_assign' || $key == 'fk_user_create' || $key == 'fk_project') {
  355. if ($search[$key] > 0) {
  356. $sql .= natural_search($key, $search[$key], 2);
  357. }
  358. continue;
  359. } elseif ($key == 'type_code') {
  360. $newarrayoftypecodes = is_array($search[$key]) ? $search[$key] : (!empty($search[$key]) ? explode(',', $search[$key]) : array());
  361. if (count($newarrayoftypecodes)) {
  362. $sql .= natural_search($key, join(',', $newarrayoftypecodes), 3);
  363. }
  364. continue;
  365. }
  366. $mode_search = ((!empty($object->fields[$key]) && ($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))) ? 1 : 0);
  367. // $search[$key] can be an array of values, or a string. We add filter if array not empty or if it is a string.
  368. if ((is_array($search[$key]) && !empty($search[$key])) || (!is_array($search[$key]) && $search[$key] != '')) {
  369. $sql .= natural_search($key, $search[$key], $mode_search);
  370. }
  371. }
  372. if ($search_all) {
  373. $sql .= natural_search(array_keys($fieldstosearchall), $search_all);
  374. }
  375. if ($search_societe) {
  376. $sql .= natural_search('s.nom', $search_societe);
  377. }
  378. if ($search_fk_project > 0) {
  379. $sql .= natural_search('fk_project', $search_fk_project, 2);
  380. }
  381. if ($search_date_start) {
  382. $sql .= " AND t.datec >= '".$db->idate($search_date_start)."'";
  383. }
  384. if ($search_date_end) {
  385. $sql .= " AND t.datec <= '".$db->idate($search_date_end)."'";
  386. }
  387. if ($search_dateread_start) {
  388. $sql .= " AND t.date_read >= '".$db->idate($search_dateread_start)."'";
  389. }
  390. if ($search_dateread_end) {
  391. $sql .= " AND t.date_read <= '".$db->idate($search_dateread_end)."'";
  392. }
  393. if ($search_dateclose_start) {
  394. $sql .= " AND t.date_close >= '".$db->idate($search_dateclose_start)."'";
  395. }
  396. if ($search_dateclose_end) {
  397. $sql .= " AND t.date_close <= '".$db->idate($search_dateclose_end)."'";
  398. }
  399. if (!$user->socid && ($mode == "mine" || (!$user->admin && $conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY))) {
  400. $sql .= " AND (t.fk_user_assign = ".((int) $user->id);
  401. if (empty($conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY)) {
  402. $sql .= " OR t.fk_user_create = ".((int) $user->id);
  403. }
  404. $sql .= ")";
  405. }
  406. // Add where from extra fields
  407. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
  408. // Add where from hooks
  409. $parameters = array();
  410. $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
  411. $sql .= $hookmanager->resPrint;
  412. // Count total nb of records
  413. $nbtotalofrecords = '';
  414. if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
  415. /* The fast and low memory method to get and count full list converts the sql into a sql count */
  416. $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
  417. $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
  418. $resql = $db->query($sqlforcount);
  419. if ($resql) {
  420. $objforcount = $db->fetch_object($resql);
  421. $nbtotalofrecords = $objforcount->nbtotalofrecords;
  422. } else {
  423. dol_print_error($db);
  424. }
  425. if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 0
  426. $page = 0;
  427. $offset = 0;
  428. }
  429. $db->free($resql);
  430. }
  431. // Complete request and execute it with limit
  432. $sql .= $db->order($sortfield, $sortorder);
  433. if ($limit) {
  434. $sql .= $db->plimit($limit + 1, $offset);
  435. }
  436. $resql = $db->query($sql);
  437. if (!$resql) {
  438. dol_print_error($db);
  439. exit;
  440. }
  441. $num = $db->num_rows($resql);
  442. // Direct jump if only one record found
  443. if ($num == 1 && !empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all && !$page) {
  444. $obj = $db->fetch_object($resql);
  445. $id = $obj->rowid;
  446. header("Location: ".DOL_URL_ROOT.'/ticket/card.php?id='.$id);
  447. exit;
  448. }
  449. // Output page
  450. // --------------------------------------------------------------------
  451. llxHeader('', $title, $help_url, '', 0, 0, $morejs, $morecss, '', 'bodyforlist');
  452. if ($socid && !$projectid && !$project_ref && $user->hasRight('societe', 'lire')) {
  453. $socstat = new Societe($db);
  454. $res = $socstat->fetch($socid);
  455. if ($res > 0) {
  456. $tmpobject = $object;
  457. $object = $socstat; // $object must be of type Societe when calling societe_prepare_head
  458. $head = societe_prepare_head($socstat);
  459. $object = $tmpobject;
  460. print dol_get_fiche_head($head, 'ticket', $langs->trans("ThirdParty"), -1, 'company');
  461. dol_banner_tab($socstat, 'socid', '', ($user->socid ? 0 : 1), 'rowid', 'nom');
  462. print '<div class="fichecenter">';
  463. print '<div class="underbanner clearboth"></div>';
  464. print '<table class="border centpercent tableforfield">';
  465. // Type Prospect/Customer/Supplier
  466. print '<tr><td class="titlefield">'.$langs->trans('NatureOfThirdParty').'</td><td>';
  467. print $socstat->getTypeUrl(1);
  468. print '</td></tr>';
  469. // Customer code
  470. if ($socstat->client && !empty($socstat->code_client)) {
  471. print '<tr><td class="titlefield">';
  472. print $langs->trans('CustomerCode').'</td><td>';
  473. print showValueWithClipboardCPButton(dol_escape_htmltag($socstat->code_client));
  474. $tmpcheck = $socstat->check_codeclient();
  475. if ($tmpcheck != 0 && $tmpcheck != -5) {
  476. print ' <span class="error">('.$langs->trans("WrongCustomerCode").')</span>';
  477. }
  478. print '</td>';
  479. print '</tr>';
  480. }
  481. // Supplier code
  482. if ($socstat->fournisseur && !empty($socstat->code_fournisseur)) {
  483. print '<tr><td class="titlefield">';
  484. print $langs->trans('SupplierCode').'</td><td>';
  485. print showValueWithClipboardCPButton(dol_escape_htmltag($socstat->code_fournisseur));
  486. $tmpcheck = $socstat->check_codefournisseur();
  487. if ($tmpcheck != 0 && $tmpcheck != -5) {
  488. print ' <span class="error">('.$langs->trans("WrongSupplierCode").')</span>';
  489. }
  490. print '</td>';
  491. print '</tr>';
  492. }
  493. print '</table>';
  494. print '</div>';
  495. print dol_get_fiche_end();
  496. }
  497. }
  498. if ($projectid > 0 || $project_ref) {
  499. $projectstat = new Project($db);
  500. if ($projectstat->fetch($projectid, $project_ref) > 0) {
  501. $projectid = $projectstat->id;
  502. $projectstat->fetch_thirdparty();
  503. $savobject = $object;
  504. $object = $projectstat;
  505. // To verify role of users
  506. //$userAccess = $object->restrictedProjectArea($user,'read');
  507. $userWrite = $projectstat->restrictedProjectArea($user, 'write');
  508. //$userDelete = $object->restrictedProjectArea($user,'delete');
  509. //print "userAccess=".$userAccess." userWrite=".$userWrite." userDelete=".$userDelete;
  510. $head = project_prepare_head($projectstat);
  511. print dol_get_fiche_head($head, 'ticket', $langs->trans("Project"), -1, ($projectstat->public ? 'projectpub' : 'project'));
  512. // Project card
  513. $linkback = '<a href="'.DOL_URL_ROOT.'/projet/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
  514. $morehtmlref = '<div class="refidno">';
  515. // Title
  516. $morehtmlref .= $object->title;
  517. // Thirdparty
  518. if (!empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
  519. $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1, 'project');
  520. }
  521. $morehtmlref .= '</div>';
  522. // Define a complementary filter for search of next/prev ref.
  523. if (empty($user->rights->projet->all->lire)) {
  524. $objectsListId = $object->getProjectsAuthorizedForUser($user, 0, 0);
  525. $object->next_prev_filter = " rowid IN (".$db->sanitize(count($objectsListId) ? join(',', array_keys($objectsListId)) : '0').")";
  526. }
  527. dol_banner_tab($object, 'project_ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
  528. print '<div class="fichecenter">';
  529. print '<div class="underbanner clearboth"></div>';
  530. print '<table class="border tableforfield centpercent">';
  531. // Visibility
  532. print '<tr><td class="titlefield">'.$langs->trans("Visibility").'</td><td>';
  533. if ($projectstat->public) {
  534. print img_picto($langs->trans('SharedProject'), 'world', 'class="paddingrightonly"');
  535. print $langs->trans('SharedProject');
  536. } else {
  537. print img_picto($langs->trans('PrivateProject'), 'private', 'class="paddingrightonly"');
  538. print $langs->trans('PrivateProject');
  539. }
  540. print '</td></tr>';
  541. print "</table>";
  542. print '</div>';
  543. print dol_get_fiche_end();
  544. $object = $savobject;
  545. } else {
  546. print "ErrorRecordNotFound";
  547. }
  548. }
  549. $arrayofselected = is_array($toselect) ? $toselect : array();
  550. $param = '';
  551. if (!empty($mode)) {
  552. $param .= '&mode='.urlencode($mode);
  553. }
  554. if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
  555. $param .= '&contextpage='.urlencode($contextpage);
  556. }
  557. if ($limit > 0 && $limit != $conf->liste_limit) {
  558. $param .= '&limit='.((int) $limit);
  559. }
  560. foreach ($search as $key => $val) {
  561. if (is_array($search[$key])) {
  562. foreach ($search[$key] as $skey) {
  563. if ($skey != '') {
  564. $param .= '&search_'.$key.'[]='.urlencode($skey);
  565. }
  566. }
  567. } elseif (preg_match('/(_dtstart|_dtend)$/', $key) && !empty($val)) {
  568. $param .= '&search_'.$key.'month='.((int) GETPOST('search_'.$key.'month', 'int'));
  569. $param .= '&search_'.$key.'day='.((int) GETPOST('search_'.$key.'day', 'int'));
  570. $param .= '&search_'.$key.'year='.((int) GETPOST('search_'.$key.'year', 'int'));
  571. } elseif ($search[$key] != '') {
  572. $param .= '&search_'.$key.'='.urlencode($search[$key]);
  573. }
  574. }
  575. if ($optioncss != '') {
  576. $param .= '&optioncss='.urlencode($optioncss);
  577. }
  578. // Add $param from extra fields
  579. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
  580. // Add $param from hooks
  581. $parameters = array();
  582. $reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object); // Note that $action and $object may have been modified by hook
  583. $param .= $hookmanager->resPrint;
  584. if ($socid > 0) {
  585. $param .= '&socid='.urlencode($socid);
  586. }
  587. if ($search_societe) {
  588. $param .= '&search_societe='.urlencode($search_societe);
  589. }
  590. if ($projectid > 0) {
  591. $param .= '&projectid='.urlencode($projectid);
  592. }
  593. if ($search_date_start) {
  594. $tmparray = dol_getdate($search_date_start);
  595. $param .= '&search_date_startday='.urlencode($tmparray['mday']);
  596. $param .= '&search_date_startmonth='.urlencode($tmparray['mon']);
  597. $param .= '&search_date_startyear='.urlencode($tmparray['year']);
  598. }
  599. if ($search_date_end) {
  600. $tmparray = dol_getdate($search_date_end);
  601. $param .= '&search_date_endday='.urlencode($tmparray['mday']);
  602. $param .= '&search_date_endmonth='.urlencode($tmparray['mon']);
  603. $param .= '&search_date_endyear='.urlencode($tmparray['year']);
  604. }
  605. if ($search_dateread_start) {
  606. $tmparray = dol_getdate($search_dateread_start);
  607. $param .= '&search_dateread_startday='.urlencode($tmparray['mday']);
  608. $param .= '&search_dateread_startmonth='.urlencode($tmparray['mon']);
  609. $param .= '&search_dateread_startyear='.urlencode($tmparray['year']);
  610. }
  611. if ($search_dateread_end) {
  612. $tmparray = dol_getdate($search_dateread_end);
  613. $param .= '&search_dateread_endday='.urlencode($tmparray['mday']);
  614. $param .= '&search_dateread_endmonth='.urlencode($tmparray['mon']);
  615. $param .= '&search_dateread_endyear='.urlencode($tmparray['year']);
  616. }
  617. if ($search_dateclose_start) {
  618. $tmparray = dol_getdate($search_dateclose_start);
  619. $param .= '&search_dateclose_startday='.urlencode($tmparray['mday']);
  620. $param .= '&search_dateclose_startmonth='.urlencode($tmparray['mon']);
  621. $param .= '&search_dateclose_startyear='.urlencode($tmparray['year']);
  622. }
  623. if ($search_dateclose_end) {
  624. $tmparray = dol_getdate($search_dateclose_end);
  625. $param .= '&search_date_endday='.urlencode($tmparray['mday']);
  626. $param .= '&search_date_endmonth='.urlencode($tmparray['mon']);
  627. $param .= '&search_date_endyear='.urlencode($tmparray['year']);
  628. }
  629. // List of mass actions available
  630. $arrayofmassactions = array(
  631. //'presend'=>img_picto('', 'email', 'class="pictofixedwidth"').$langs->trans("SendByMail"),
  632. //'builddoc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("PDFMerge"),
  633. );
  634. if ($permissiontoadd) {
  635. $arrayofmassactions['presendonclose'] = img_picto('', 'close_title', 'class="pictofixedwidth"').$langs->trans("Close");
  636. $arrayofmassactions['reopen'] = img_picto('', 'folder-open', 'class="pictofixedwidth"').$langs->trans("ReOpen");
  637. }
  638. if ($permissiontodelete) {
  639. $arrayofmassactions['predelete'] = img_picto('', 'delete', 'class="pictofixedwidth"').$langs->trans("Delete");
  640. }
  641. if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend', 'predelete'))) {
  642. $arrayofmassactions = array();
  643. }
  644. $massactionbutton = $form->selectMassAction('', $arrayofmassactions);
  645. print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">'."\n";
  646. if ($optioncss != '') {
  647. print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
  648. }
  649. print '<input type="hidden" name="token" value="'.newToken().'">';
  650. print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
  651. print '<input type="hidden" name="action" value="list">';
  652. print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
  653. print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
  654. print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
  655. print '<input type="hidden" name="mode" value="'.$mode.'" >';
  656. if ($socid) {
  657. print '<input type="hidden" name="socid" value="'.$socid.'" >';
  658. }
  659. if ($projectid) {
  660. print '<input type="hidden" name="projectid" value="'.$projectid.'" >';
  661. }
  662. $url = DOL_URL_ROOT.'/ticket/card.php?action=create'.($socid ? '&socid='.$socid : '').($projectid ? '&origin=projet_project&originid='.$projectid : '');
  663. if (!empty($socid)) {
  664. $url .= '&socid='.$socid;
  665. }
  666. $newcardbutton = '';
  667. $newcardbutton .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', $_SERVER["PHP_SELF"].'?mode=common'.preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ((empty($mode) || $mode == 'common') ? 2 : 1), array('morecss'=>'reposition'));
  668. $newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER["PHP_SELF"].'?mode=kanban'.preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ($mode == 'kanban' ? 2 : 1), array('morecss'=>'reposition'));
  669. $newcardbutton .= dolGetButtonTitle($langs->trans('NewTicket'), '', 'fa fa-plus-circle', $url, '', $user->rights->ticket->write);
  670. $picto = 'ticket';
  671. if ($socid > 0) {
  672. $picto = '';
  673. }
  674. print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, $picto, 0, $newcardbutton, '', $limit, 0, 0, 1);
  675. if ($mode == 'mine') {
  676. print '<div class="opacitymedium">'.$langs->trans('TicketAssignedToMeInfos').'</div><br>';
  677. }
  678. // Add code for pre mass action (confirmation or email presend form)
  679. $topicmail = "SendTicketRef";
  680. $modelmail = "ticket";
  681. $objecttmp = new Ticket($db);
  682. $trackid = 'tic'.$object->id;
  683. include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
  684. // confirm auto send on close
  685. if ($massaction == 'presendonclose') {
  686. $hidden_form = array([
  687. "type" => "hidden",
  688. "name" => "massaction",
  689. "value" => "close"
  690. ]);
  691. $selectedchoice = (!empty($conf->global->TICKET_NOTIFY_AT_CLOSING)) ? "yes" : "no";
  692. print $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans("ConfirmMassTicketClosingSendEmail"), $langs->trans("ConfirmMassTicketClosingSendEmailQuestion"), 'confirm_send_close', $hidden_form, $selectedchoice, 0, 200, 500, 1);
  693. }
  694. if ($search_all) {
  695. $setupstring = '';
  696. foreach ($fieldstosearchall as $key => $val) {
  697. $fieldstosearchall[$key] = $langs->trans($val);
  698. $setupstring .= $key."=".$val.";";
  699. }
  700. print '<!-- Search done like if TICKET_QUICKSEARCH_ON_FIELDS = '.$setupstring.' -->'."\n";
  701. print '<div class="divsearchfieldfilter">'.$langs->trans("FilterOnInto", $search_all).join(', ', $fieldstosearchall).'</div>'."\n";
  702. }
  703. $moreforfilter = '';
  704. /*$moreforfilter.='<div class="divsearchfield">';
  705. $moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escape_htmltag($search_myfield).'">';
  706. $moreforfilter.= '</div>';*/
  707. $parameters = array();
  708. $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook
  709. if (empty($reshook)) {
  710. $moreforfilter .= $hookmanager->resPrint;
  711. } else {
  712. $moreforfilter = $hookmanager->resPrint;
  713. }
  714. if (!empty($moreforfilter)) {
  715. print '<div class="liste_titre liste_titre_bydiv centpercent">';
  716. print $moreforfilter;
  717. print '</div>';
  718. }
  719. $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
  720. $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')); // This also change content of $arrayfields
  721. $selectedfields .= (count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : '');
  722. print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you dont need reserved height for your table
  723. print '<div class="div-table-responsive-inside">';
  724. print '<table class="tagtable nobottomiftotal liste'.($moreforfilter ? " listwithfilterbefore" : "").'">'."\n";
  725. // Fields title search
  726. // --------------------------------------------------------------------
  727. print '<tr class="liste_titre_filter">';
  728. // Action column
  729. if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  730. print '<td class="liste_titre maxwidthsearch center">';
  731. $searchpicto = $form->showFilterButtons('left');
  732. print $searchpicto;
  733. print '</td>';
  734. }
  735. foreach ($object->fields as $key => $val) {
  736. $searchkey = empty($search[$key]) ? '' : $search[$key];
  737. $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']);
  738. if ($key == 'fk_statut') {
  739. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  740. } elseif (in_array($val['type'], array('date', 'datetime', 'timestamp'))) {
  741. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  742. } elseif (in_array($val['type'], array('timestamp'))) {
  743. $cssforfield .= ($cssforfield ? ' ' : '').'nowrap';
  744. } elseif (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && $key != 'rowid' && $val['label'] != 'TechnicalID' && empty($val['arrayofkeyval'])) {
  745. $cssforfield .= ($cssforfield ? ' ' : '').'right';
  746. }
  747. if (!empty($arrayfields['t.'.$key]['checked'])) {
  748. if ($key == 'progress') {
  749. print '<td class="liste_titre right'.($cssforfield ? ' '.$cssforfield : '').'">';
  750. print '<input type="text" class="flat maxwidth50" name="search_'.$key.'" value="'.dol_escape_htmltag(empty($search[$key]) ? '' : $search[$key]).'">';
  751. print '</td>';
  752. } elseif ($key == 'type_code') {
  753. print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').'">';
  754. $formTicket->selectTypesTickets(dol_escape_htmltag(empty($search[$key]) ? '' : $search[$key]), 'search_'.$key, '', 2, 1, 1, 0, (!empty($val['css']) ? $val['css'] : 'maxwidth150'), 1);
  755. print '</td>';
  756. } elseif ($key == 'category_code') {
  757. print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').'">';
  758. $formTicket->selectGroupTickets(dol_escape_htmltag(empty($search[$key]) ? '' : $search[$key]), 'search_'.$key, '', 2, 1, 1, 0, (!empty($val['css']) ? $val['css'] : 'maxwidth150'));
  759. print '</td>';
  760. } elseif ($key == 'severity_code') {
  761. print '<td class="liste_titre center'.($cssforfield ? ' '.$cssforfield : '').'">';
  762. $formTicket->selectSeveritiesTickets(dol_escape_htmltag(empty($search[$key]) ? '' : $search[$key]), 'search_'.$key, '', 2, 1, 1, 0, (!empty($val['css']) ? $val['css'] : 'maxwidth150'));
  763. print '</td>';
  764. } elseif ($key == 'fk_user_assign' || $key == 'fk_user_create') {
  765. print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').'">';
  766. print $form->select_dolusers((empty($search[$key]) ? '' : $search[$key]), 'search_'.$key, 1, null, 0, '', '', '0', 0, 0, '', 0, '', (!empty($val['css']) ? $val['css'] : 'maxwidth100'));
  767. print '</td>';
  768. } elseif ($key == 'fk_statut') {
  769. $arrayofstatus = array();
  770. $arrayofstatus['openall'] = '-- '.$langs->trans('OpenAll').' --';
  771. foreach ($object->statuts_short as $key2 => $val2) {
  772. if ($key2 == Ticket::STATUS_CLOSED) {
  773. $arrayofstatus['closeall'] = '-- '.$langs->trans('ClosedAll').' --';
  774. }
  775. $arrayofstatus[$key2] = $val2;
  776. }
  777. print '<td class="liste_titre center parentonrightofpage'.($cssforfield ? ' '.$cssforfield : '').'">';
  778. //var_dump($arrayofstatus);
  779. //var_dump($search['fk_statut']);
  780. //var_dump(array_values($search[$key]));
  781. $selectedarray = null;
  782. if (!empty($search[$key])) {
  783. $selectedarray = array_values($search[$key]);
  784. }
  785. print Form::multiselectarray('search_fk_statut', $arrayofstatus, $selectedarray, 0, 0, 'search_status width150 onrightofpage', 1, 0, '', '', '');
  786. print '</td>';
  787. } elseif ($key == "fk_soc") {
  788. print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').'"><input type="text" class="flat maxwidth75" name="search_societe" value="'.dol_escape_htmltag($search_societe).'"></td>';
  789. } elseif ($key == "datec" || $key == 'date_read' || $key == 'date_close') {
  790. print '<td class="liste_titre center">';
  791. print '<div class="nowrap">';
  792. switch ($key) {
  793. case 'datec':
  794. print $form->selectDate($search_date_start ?: -1, 'search_date_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
  795. break;
  796. case 'date_read':
  797. print $form->selectDate($search_dateread_start ?: -1, 'search_dateread_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
  798. break;
  799. case 'date_close':
  800. print $form->selectDate($search_dateclose_start ?: -1, 'search_dateclose_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
  801. }
  802. print '</div>';
  803. print '<div class="nowrap">';
  804. switch ($key) {
  805. case 'datec':
  806. print $form->selectDate($search_date_end ?: -1, 'search_date_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
  807. break;
  808. case 'date_read':
  809. print $form->selectDate($search_dateread_end ?: -1, 'search_dateread_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
  810. break;
  811. case 'date_close':
  812. print $form->selectDate($search_dateclose_end ?: -1, 'search_dateclose_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
  813. }
  814. print '</div>';
  815. print '</td>';
  816. } else {
  817. print '<td class="liste_titre'.($cssforfield ? ' '.$cssforfield : '').'">';
  818. if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  819. print $form->selectarray('search_'.$key, $val['arrayofkeyval'], $search[$key], $val['notnull'], 0, 0, '', 1, 0, 0, '', 'maxwidth100', 1);
  820. } elseif (strpos($val['type'], 'integer:') === 0) {
  821. print $object->showInputField($val, $key, $search[$key], '', '', 'search_', 'maxwidth150', 1);
  822. } elseif (!preg_match('/^(date|timestamp)/', $val['type'])) {
  823. print '<input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag(empty($search[$key]) ? '' : $search[$key]).'">';
  824. }
  825. print '</td>';
  826. }
  827. }
  828. }
  829. // Extra fields
  830. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
  831. // Fields from hook
  832. $parameters = array('arrayfields'=>$arrayfields);
  833. $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  834. print $hookmanager->resPrint;
  835. // Action column
  836. if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  837. print '<td class="liste_titre center maxwidthsearch">';
  838. $searchpicto = $form->showFilterButtons();
  839. print $searchpicto;
  840. print '</td>';
  841. }
  842. print '</tr>'."\n";
  843. $totalarray = array();
  844. $totalarray['nbfield'] = 0;
  845. // Fields title label
  846. // --------------------------------------------------------------------
  847. print '<tr class="liste_titre">';
  848. if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  849. print getTitleFieldOfList(($mode != 'kanban' ? $selectedfields : ''), 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n";
  850. $totalarray['nbfield']++;
  851. }
  852. foreach ($object->fields as $key => $val) {
  853. $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']);
  854. if ($key == 'fk_statut' || $key == 'severity_code') {
  855. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  856. } elseif (in_array($val['type'], array('date', 'datetime', 'timestamp'))) {
  857. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  858. } elseif (in_array($val['type'], array('timestamp'))) {
  859. $cssforfield .= ($cssforfield ? ' ' : '').'nowrap';
  860. } elseif (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && $key != 'rowid' && $val['label'] != 'TechnicalID' && empty($val['arrayofkeyval'])) {
  861. $cssforfield .= ($cssforfield ? ' ' : '').'right';
  862. }
  863. $cssforfield = preg_replace('/small\s*/', '', $cssforfield); // the 'small' css must not be used for the title label
  864. if (!empty($arrayfields['t.'.$key]['checked'])) {
  865. print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($cssforfield ? 'class="'.$cssforfield.'"' : ''), $sortfield, $sortorder, ($cssforfield ? $cssforfield.' ' : ''), 0, (empty($val['helplist']) ? '' : $val['helplist']))."\n";
  866. $totalarray['nbfield']++;
  867. }
  868. }
  869. // Extra fields
  870. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
  871. // Hook fields
  872. $parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder, 'totalarray'=>&$totalarray);
  873. $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  874. print $hookmanager->resPrint;
  875. if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  876. print getTitleFieldOfList(($mode != 'kanban' ? $selectedfields : ''), 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n";
  877. $totalarray['nbfield']++;
  878. }
  879. print '</tr>'."\n";
  880. // Detect if we need a fetch on each output line
  881. $needToFetchEachLine = 0;
  882. if (isset($extrafields->attributes[$object->table_element]['computed']) && is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0) {
  883. foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val) {
  884. if ($val && preg_match('/\$object/', $val)) {
  885. $needToFetchEachLine++; // There is at least one compute field that use $object
  886. }
  887. }
  888. }
  889. // Loop on record
  890. // --------------------------------------------------------------------
  891. $i = 0;
  892. $savnbfield = $totalarray['nbfield'];
  893. $totalarray = array();
  894. $totalarray['nbfield'] = 0;
  895. $imaxinloop = ($limit ? min($num, $limit) : $num);
  896. $cacheofoutputfield = array();
  897. while ($i < $imaxinloop) {
  898. $obj = $db->fetch_object($resql);
  899. if (empty($obj)) {
  900. break; // Should not happen
  901. }
  902. // Store properties in $object
  903. $object->setVarsFromFetchObj($obj);
  904. $object->type_code = $obj->type_code;
  905. $object->status = $object->fk_statut; // fk_statut is deprecated
  906. if ($mode == 'kanban') {
  907. if ($i == 0) {
  908. print '<tr><td colspan="'.$savnbfield.'">';
  909. print '<div class="box-flex-container kanban">';
  910. }
  911. // get infos needed from object
  912. // TODO Create a cache on users
  913. $arraydata = array();
  914. if ($obj->fk_user_assign > 0) {
  915. $user_temp->fetch($obj->fk_user_assign);
  916. $arraydata['user_assignment'] = $user_temp->getNomUrl(-3);
  917. }
  918. $arraydata['selected'] = in_array($object->id, $arrayofselected);
  919. // Output Kanban
  920. print $object->getKanbanView('', $arraydata);
  921. if ($i == ($imaxinloop - 1)) {
  922. print '</div>';
  923. print '</td></tr>';
  924. }
  925. } else {
  926. // Show here line of result
  927. $j = 0;
  928. print '<tr data-rowid="'.$object->id.'" class="oddeven">';
  929. // Action column
  930. if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  931. print '<td class="nowrap center">';
  932. if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
  933. $selected = 0;
  934. if (in_array($object->id, $arrayofselected)) {
  935. $selected = 1;
  936. }
  937. print '<input id="cb'.$object->id.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$object->id.'"'.($selected ? ' checked="checked"' : '').'>';
  938. }
  939. print '</td>';
  940. if (!$i) {
  941. $totalarray['nbfield']++;
  942. }
  943. }
  944. foreach ($object->fields as $key => $val) {
  945. $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']);
  946. if (in_array($val['type'], array('date', 'datetime', 'timestamp'))) {
  947. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  948. }
  949. if (in_array($val['type'], array('timestamp'))) {
  950. $cssforfield .= ($cssforfield ? ' ' : '').'nowrap';
  951. }
  952. if (in_array($key, array('ref', 'fk_project'))) {
  953. $cssforfield .= ($cssforfield ? ' ' : '').'nowraponall';
  954. }
  955. if ($key == 'fk_statut' || $key == 'severity_code') {
  956. $cssforfield .= ($cssforfield ? ' ' : '').'center';
  957. }
  958. if (!empty($arrayfields['t.'.$key]['checked'])) {
  959. print '<td';
  960. if ($cssforfield || (array_key_exists('css', $val) && $val['css'])) {
  961. print ' class="';
  962. }
  963. print $cssforfield;
  964. if ($cssforfield && array_key_exists('css', $val) && $val['css']) {
  965. print ' ';
  966. }
  967. if (array_key_exists('css', $val)) {
  968. print $val['css'];
  969. }
  970. if ($cssforfield || (array_key_exists('css', $val) && $val['css'])) {
  971. print '"';
  972. }
  973. print '>';
  974. if ($key == 'fk_statut') {
  975. print $object->getLibStatut(5);
  976. } elseif ($key == 'subject') {
  977. $s = $obj->subject;
  978. print '<span title="'.dol_escape_htmltag($s).'">';
  979. print dol_escape_htmltag($s);
  980. print '</span>';
  981. } elseif ($key == 'type_code') {
  982. $s = $langs->getLabelFromKey($db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code);
  983. print '<span title="'.dol_escape_htmltag($s).'">';
  984. print $s;
  985. print '</span>';
  986. } elseif ($key == 'category_code') {
  987. $s = $langs->getLabelFromKey($db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code);
  988. print '<span title="'.dol_escape_htmltag($s).'">';
  989. print $s;
  990. print '</span>';
  991. } elseif ($key == 'severity_code') {
  992. $s = $langs->getLabelFromKey($db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code);
  993. print '<span title="'.dol_escape_htmltag($s).'">';
  994. print $s;
  995. print '</span>';
  996. } elseif ($key == 'tms') {
  997. print dol_print_date($db->jdate($obj->$key), 'dayhour', 'tzuser');
  998. } elseif ($key == 'fk_user_create') {
  999. if ($object->fk_user_create > 0) {
  1000. if (isset($conf->cache['user'][$object->fk_user_create])) {
  1001. $user_temp = $conf->cache['user'][$object->fk_user_create];
  1002. } else {
  1003. $user_temp = new User($db);
  1004. $user_temp->fetch($object->fk_user_create);
  1005. $conf->cache['user'][$object->fk_user_create] = $user_temp;
  1006. }
  1007. print $user_temp->getNomUrl(-1);
  1008. }
  1009. } elseif ($key == 'fk_user_assign') {
  1010. if ($object->fk_user_assign > 0) {
  1011. if (isset($conf->cache['user'][$object->fk_user_assign])) {
  1012. $user_temp = $conf->cache['user'][$object->fk_user_assign];
  1013. } else {
  1014. $user_temp = new User($db);
  1015. $user_temp->fetch($object->fk_user_assign);
  1016. $conf->cache['user'][$object->fk_user_assign] = $user_temp;
  1017. }
  1018. print $user_temp->getNomUrl(-1);
  1019. }
  1020. } elseif (in_array($val['type'], array('date', 'datetime', 'timestamp'))) {
  1021. print $object->showOutputField($val, $key, $db->jdate($obj->$key), '');
  1022. } elseif ($key == 'ref') {
  1023. print $object->showOutputField($val, $key, $obj->$key, '');
  1024. // display a warning on untreated tickets
  1025. $is_open = ($object->status != Ticket::STATUS_CLOSED && $object->status != Ticket::STATUS_CANCELED );
  1026. $should_show_warning = (!empty($conf->global->TICKET_DELAY_SINCE_LAST_RESPONSE) || !empty($conf->global->TICKET_DELAY_BEFORE_FIRST_RESPONSE));
  1027. if ($is_open && $should_show_warning) {
  1028. $date_last_msg_sent = (int) $object->date_last_msg_sent;
  1029. $hour_diff = ($now - $date_last_msg_sent) / 3600 ;
  1030. if (!empty($conf->global->TICKET_DELAY_BEFORE_FIRST_RESPONSE && $date_last_msg_sent == 0)) {
  1031. $creation_date = $object->datec;
  1032. $hour_diff_creation = ($now - $creation_date) / 3600 ;
  1033. if ($hour_diff_creation > $conf->global->TICKET_DELAY_BEFORE_FIRST_RESPONSE) {
  1034. print " " . img_picto($langs->trans('Late') . ' : ' . $langs->trans('TicketsDelayForFirstResponseTooLong', $conf->global->TICKET_DELAY_BEFORE_FIRST_RESPONSE), 'warning', 'style="color: red;"', false, 0, 0, '', '');
  1035. }
  1036. } elseif (!empty($conf->global->TICKET_DELAY_SINCE_LAST_RESPONSE) && $hour_diff > $conf->global->TICKET_DELAY_SINCE_LAST_RESPONSE) {
  1037. print " " . img_picto($langs->trans('Late') . ' : ' . $langs->trans('TicketsDelayFromLastResponseTooLong', $conf->global->TICKET_DELAY_SINCE_LAST_RESPONSE), 'warning');
  1038. }
  1039. }
  1040. } else { // Example: key=fk_soc, obj->key=123 val=array('type'=>'integer', ...
  1041. $tmp = explode(':', $val['type']);
  1042. if ($tmp[0] == 'integer' && !empty($tmp[1]) && class_exists($tmp[1])) {
  1043. // It is a type of an foreign field. We will try to reduce the number of fetch that the showOutputField is making.
  1044. //var_dump('eeee-'.$key.'-'.$obj->$key.'-'.$val['type']);
  1045. if ($key && $obj->$key && $val['type'] && array_key_exists($key.'-'.$obj->$key.'-'.$val['type'], $cacheofoutputfield)) {
  1046. $result = $cacheofoutputfield[$key.'-'.$obj->$key.'-'.$val['type']];
  1047. } else {
  1048. $result = $object->showOutputField($val, $key, $obj->$key, '');
  1049. $cacheofoutputfield[$key.'-'.$obj->$key.'-'.$val['type']] = $result;
  1050. }
  1051. } else {
  1052. $result = $object->showOutputField($val, $key, $obj->$key, '');
  1053. }
  1054. print $result;
  1055. }
  1056. print '</td>';
  1057. if (!$i) {
  1058. $totalarray['nbfield']++;
  1059. }
  1060. if (!empty($val['isameasure']) && $val['isameasure'] == 1) {
  1061. if (!$i) {
  1062. $totalarray['pos'][$totalarray['nbfield']] = 't.'.$key;
  1063. }
  1064. if (!isset($totalarray['val'])) {
  1065. $totalarray['val'] = array();
  1066. }
  1067. if (!isset($totalarray['val']['t.'.$key])) {
  1068. $totalarray['val']['t.'.$key] = 0;
  1069. }
  1070. $totalarray['val']['t.'.$key] += $object->$key;
  1071. }
  1072. }
  1073. }
  1074. // Extra fields
  1075. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
  1076. // Fields from hook
  1077. $parameters = array('arrayfields'=>$arrayfields, 'object'=>$object, 'obj'=>$obj, 'i'=>$i, 'totalarray'=>&$totalarray);
  1078. $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  1079. print $hookmanager->resPrint;
  1080. // Action column
  1081. if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
  1082. print '<td class="nowrap center">';
  1083. if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
  1084. $selected = 0;
  1085. if (in_array($object->id, $arrayofselected)) {
  1086. $selected = 1;
  1087. }
  1088. print '<input id="cb'.$object->id.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$object->id.'"'.($selected ? ' checked="checked"' : '').'>';
  1089. }
  1090. print '</td>';
  1091. }
  1092. if (!$i) {
  1093. $totalarray['nbfield']++;
  1094. }
  1095. print '</tr>'."\n";
  1096. }
  1097. $i++;
  1098. }
  1099. // Show total line
  1100. include DOL_DOCUMENT_ROOT.'/core/tpl/list_print_total.tpl.php';
  1101. // If no record found
  1102. if ($num == 0) {
  1103. $colspan = 1;
  1104. foreach ($arrayfields as $key => $val) {
  1105. if (!empty($val['checked'])) {
  1106. $colspan++;
  1107. }
  1108. }
  1109. print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span></td></tr>';
  1110. }
  1111. $db->free($resql);
  1112. $parameters = array('arrayfields'=>$arrayfields, 'sql'=>$sql);
  1113. $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object); // Note that $action and $object may have been modified by hook
  1114. print $hookmanager->resPrint;
  1115. print '</table>'."\n";
  1116. print '</div>'."\n";
  1117. print '</div>'."\n"; // end div-responsive-inside
  1118. print '</form>'."\n";
  1119. if (in_array('builddoc', $arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords)) {
  1120. $hidegeneratedfilelistifempty = 1;
  1121. if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) {
  1122. $hidegeneratedfilelistifempty = 0;
  1123. }
  1124. require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
  1125. $formfile = new FormFile($db);
  1126. // Show list of available documents
  1127. $urlsource = $_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
  1128. $urlsource .= str_replace('&amp;', '&', $param);
  1129. $filedir = $diroutputmassaction;
  1130. $genallowed = $permissiontoread;
  1131. $delallowed = $permissiontoadd;
  1132. print $formfile->showdocuments('massfilesarea_ticket', '', $filedir, $urlsource, 0, $delallowed, '', 1, 1, 0, 48, 1, $param, $title, '', '', '', null, $hidegeneratedfilelistifempty);
  1133. }
  1134. // End of page
  1135. llxFooter();
  1136. $db->close();