dolibarr 23.0.3
ganttview.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
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
28require "../main.inc.php";
29require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
30require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
31require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
32require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
33require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
34require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
35require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
36require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
45$id = GETPOST('id', 'intcomma');
46$ref = GETPOST('ref', 'alpha');
47
48$mode = GETPOST('mode', 'alpha');
49$mine = ($mode == 'mine' ? 1 : 0);
50
51$object = new Project($db);
52
53include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'
54if (getDolGlobalString('PROJECT_ALLOW_COMMENT_ON_PROJECT') && method_exists($object, 'fetchComments') && empty($object->comments)) {
55 $object->fetchComments();
56}
57
58// Security check
59$socid = 0;
60//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignment.
61$result = restrictedArea($user, 'projet', $id, 'projet&project');
62
63// Load translation files required by the page
64$langs->loadlangs(array('users', 'projects'));
65
66
67/*
68 * Actions
69 */
70
71// None
72
73
74/*
75 * View
76 */
77
78$form = new Form($db);
79$formother = new FormOther($db);
80$userstatic = new User($db);
81$companystatic = new Societe($db);
82$contactstatic = new Contact($db);
83$task = new Task($db);
84
85$arrayofcss = array('/includes/jsgantt/jsgantt.css');
86$arrayofjs = [];
87if (!empty($conf->use_javascript_ajax)) {
88 $arrayofjs = [
89 '/includes/jsgantt/jsgantt.js',
90 '/projet/jsgantt_language.js.php?lang='.$langs->defaultlang
91 ];
92}
93
94//$title=$langs->trans("Gantt").($object->ref?' - '.$object->ref.' '.$object->name:'');
95$title = $langs->trans("Gantt");
96if (getDolGlobalString('MAIN_HTML_TITLE') && preg_match('/projectnameonly/', getDolGlobalString('MAIN_HTML_TITLE')) && $object->name) {
97 $title = ($object->ref ? $object->ref.' '.$object->name.' - ' : '').$langs->trans("Gantt");
98}
99$help_url = "EN:Module_Projects|FR:Module_Projets|ES:M&oacute;dulo_Proyectos";
100
101llxHeader("", $title, $help_url, '', 0, 0, $arrayofjs, $arrayofcss, '', 'mod-project page-card_ganttview');
102
103$userWrite = 0;
104
105if (($id > 0 && is_numeric($id)) || !empty($ref)) {
106 // To verify role of users
107 //$userAccess = $object->restrictedProjectArea($user,'read');
108 $userWrite = $object->restrictedProjectArea($user, 'write');
109 //$userDelete = $object->restrictedProjectArea($user,'delete');
110 //print "userAccess=".$userAccess." userWrite=".$userWrite." userDelete=".$userDelete;
111
112 $tab = 'tasks';
113
114 $head = project_prepare_head($object);
115 print dol_get_fiche_head($head, $tab, $langs->trans("Project"), -1, ($object->public ? 'projectpub' : 'project'));
116
117 $param = ($mode == 'mine' ? '&mode=mine' : '');
118
119
120
121 // Project card
122
123 if (!empty($_SESSION['pageforbacktolist']) && !empty($_SESSION['pageforbacktolist']['project'])) {
124 $tmpurl = $_SESSION['pageforbacktolist']['project'];
125 $tmpurl = preg_replace('/__SOCID__/', (string) $object->socid, $tmpurl);
126 $linkback = '<a href="'.$tmpurl.(preg_match('/\?/', $tmpurl) ? '&' : '?'). 'restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
127 } else {
128 $linkback = '<a href="'.DOL_URL_ROOT.'/projet/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
129 }
130
131 $morehtmlref = '<div class="refidno">';
132 // Title
133 $morehtmlref .= $object->title;
134 // Thirdparty
135 if (!empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
136 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1, 'project');
137 }
138 $morehtmlref .= '</div>';
139
140 // Define a complementary filter for search of next/prev ref.
141 if (!$user->hasRight('projet', 'all', 'lire')) {
142 $objectsListId = $object->getProjectsAuthorizedForUser($user, 0, 0);
143 $object->next_prev_filter = "rowid:IN:".$db->sanitize(count($objectsListId) ? implode(',', array_keys($objectsListId)) : '0');
144 }
145
146 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
147
148
149 print '<div class="fichecenter">';
150 print '<div class="fichehalfleft">';
151 print '<div class="underbanner clearboth"></div>';
152
153 print '<table class="border tableforfield centpercent">';
154
155 // Usage
156 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES') || !getDolGlobalString('PROJECT_HIDE_TASKS') || isModEnabled('eventorganization')) {
157 print '<tr><td class="tdtop">';
158 print $langs->trans("Usage");
159 print '</td>';
160 print '<td>';
161 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
162 print '<input type="checkbox" disabled name="usage_opportunity"'.(GETPOSTISSET('usage_opportunity') ? (GETPOST('usage_opportunity', 'alpha') != '' ? ' checked="checked"' : '') : ($object->usage_opportunity ? ' checked="checked"' : '')).'"> ';
163 $htmltext = $langs->trans("ProjectFollowOpportunity");
164 print $form->textwithpicto($langs->trans("ProjectFollowOpportunity"), $htmltext);
165 print '<br>';
166 }
167 if (!getDolGlobalString('PROJECT_HIDE_TASKS')) {
168 print '<input type="checkbox" disabled name="usage_task"'.(GETPOSTISSET('usage_task') ? (GETPOST('usage_task', 'alpha') != '' ? ' checked="checked"' : '') : ($object->usage_task ? ' checked="checked"' : '')).'"> ';
169 $htmltext = $langs->trans("ProjectFollowTasks");
170 print $form->textwithpicto($langs->trans("ProjectFollowTasks"), $htmltext);
171 print '<br>';
172 }
173 if (!getDolGlobalString('PROJECT_HIDE_TASKS') && getDolGlobalString('PROJECT_BILL_TIME_SPENT')) {
174 print '<input type="checkbox" disabled name="usage_bill_time"'.(GETPOSTISSET('usage_bill_time') ? (GETPOST('usage_bill_time', 'alpha') != '' ? ' checked="checked"' : '') : ($object->usage_bill_time ? ' checked="checked"' : '')).'"> ';
175 $htmltext = $langs->trans("ProjectBillTimeDescription");
176 print $form->textwithpicto($langs->trans("BillTime"), $htmltext);
177 print '<br>';
178 }
179 if (isModEnabled('eventorganization')) {
180 print '<input type="checkbox" disabled name="usage_organize_event"'.(GETPOSTISSET('usage_organize_event') ? (GETPOST('usage_organize_event', 'alpha') != '' ? ' checked="checked"' : '') : ($object->usage_organize_event ? ' checked="checked"' : '')).'"> ';
181 $htmltext = $langs->trans("EventOrganizationDescriptionLong");
182 print $form->textwithpicto($langs->trans("ManageOrganizeEvent"), $htmltext);
183 }
184 print '</td></tr>';
185 }
186
187 // Visibility
188 print '<tr><td class="titlefield">'.$langs->trans("Visibility").'</td><td>';
189 if ($object->public) {
190 print img_picto($langs->trans('SharedProject'), 'world', 'class="paddingrightonly"');
191 print $langs->trans('SharedProject');
192 } else {
193 print img_picto($langs->trans('PrivateProject'), 'private', 'class="paddingrightonly"');
194 print $langs->trans('PrivateProject');
195 }
196 print '</td></tr>';
197
198 // Budget
199 print '<tr><td>'.$langs->trans("Budget").'</td><td>';
200 if (!is_null($object->budget_amount) && strcmp($object->budget_amount, '')) {
201 print price($object->budget_amount, 0, $langs, 1, 0, 0, $conf->currency);
202 }
203 print '</td></tr>';
204
205 // Date start - end project
206 print '<tr><td>'.$langs->trans("DateStart").' - '.$langs->trans("DateEnd").'</td><td>';
207 $start = dol_print_date($object->date_start, 'day');
208 print($start ? $start : '?');
209 $end = dol_print_date($object->date_end, 'day');
210 print ' - ';
211 print($end ? $end : '?');
212 if ($object->hasDelay()) {
213 print img_warning("Late");
214 }
215 print '</td></tr>';
216
217 // Other attributes
218 $cols = 2;
219 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
220
221 print '</table>';
222
223 print '</div>';
224 print '<div class="fichehalfright">';
225 print '<div class="underbanner clearboth"></div>';
226
227 print '<table class="border tableforfield centpercent">';
228
229 // Description
230 print '<td class="titlefield tdtop">'.$langs->trans("Description").'</td><td>';
231 print nl2br($object->description);
232 print '</td></tr>';
233
234 // Categories
235 if (isModEnabled('category')) {
236 print '<tr><td class="valignmiddle">'.$langs->trans("Categories").'</td><td>';
237 print $form->showCategories($object->id, Categorie::TYPE_PROJECT, 1);
238 print "</td></tr>";
239 }
240
241 print '</table>';
242
243 print '</div>';
244 print '</div>';
245
246 print '<div class="clearboth"></div>';
247
248 print dol_get_fiche_end();
249
250 print '<br>';
251}
252
253// Link to create task
254$linktocreatetaskParam = array();
255$linktocreatetaskUserRight = false;
256if ($user->hasRight('projet', 'all', 'creer') || $user->hasRight('projet', 'creer')) {
257 if ($object->public || $userWrite > 0) {
258 $linktocreatetaskUserRight = true;
259 } else {
260 $linktocreatetaskParam['attr']['title'] = $langs->trans("NotOwnerOfProject");
261 }
262}
263
264$linktocreatetask = dolGetButtonTitle($langs->trans('AddTask'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/projet/tasks.php?id='.$object->id.'&action=create'.$param.'&backtopage='.urlencode($_SERVER['PHP_SELF'].'?id='.$object->id), '', (int) $linktocreatetaskUserRight, $linktocreatetaskParam);
265
266$linktotasks = dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars paddingleft imgforviewmode', DOL_URL_ROOT.'/projet/tasks.php?id='.$object->id, '', 1, array('morecss' => 'reposition'));
267$linktotasks .= dolGetButtonTitle($langs->trans('ViewGantt'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/projet/ganttview.php?id='.$object->id.'&withproject=1', '', 1, array('morecss' => 'reposition marginleftonly btnTitleSelected'));
268
269//print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, $linktotasks, $num, $totalnboflines, 'generic', 0, '', '', 0, 1);
270print load_fiche_titre($title, $linktotasks.' &nbsp; '.$linktocreatetask, 'projecttask');
271
272
273// Get list of tasks in tasksarray and taskarrayfiltered
274// We need all tasks (even not limited to a user because a task to user
275// can have a parent that is not affected to him).
276$tasksarray = $task->getTasksArray(null, null, ($object->id ? $object->id : $id), $socid, 0);
277// We load also tasks limited to a particular user
278//$tasksrole=($_REQUEST["mode"]=='mine' ? $task->getUserRolesForProjectsOrTasks(null, $user, $object->id, 0) : '');
279//var_dump($tasksarray);
280//var_dump($tasksrole);
281
282
283if (count($tasksarray) > 0) {
284 // Show Gant diagram from $taskarray using JSGantt
285
286 $dateformat = $langs->trans("FormatDateShortJQuery"); // Used by include ganttchart.inc.php later
287 $datehourformat = $langs->trans("FormatDateShortJQuery").' '.$langs->trans("FormatHourShortJQuery"); // Used by include ganttchart.inc.php later
288 $array_contacts = array();
289 $tasks = array();
290 $task_dependencies = array();
291 $taskcursor = 0;
292 foreach ($tasksarray as $key => $val) { // Task array are sorted by "project, position, date"
293 $task->fetch($val->id, '');
294
295 $idparent = ($val->fk_task_parent ? $val->fk_task_parent : '-'.$val->fk_project); // If start with -, id is a project id
296
297 $tasks[$taskcursor]['task_id'] = $val->id;
298 $tasks[$taskcursor]['task_alternate_id'] = ($taskcursor + 1); // An id that has same order than position (required by ganttchart)
299 $tasks[$taskcursor]['task_project_id'] = $val->fk_project;
300 $tasks[$taskcursor]['task_parent'] = $idparent;
301
302 $tasks[$taskcursor]['task_is_group'] = 0;
303 $tasks[$taskcursor]['task_css'] = 'gtaskblue';
304 $tasks[$taskcursor]['task_position'] = $val->rang;
305 $tasks[$taskcursor]['task_planned_workload'] = $val->planned_workload;
306
307 if ($val->fk_task_parent != 0 && $task->hasChildren() > 0) {
308 $tasks[$taskcursor]['task_is_group'] = 1;
309 $tasks[$taskcursor]['task_css'] = 'ggroupblack';
310 //$tasks[$taskcursor]['task_css'] = 'gtaskblue';
311 } elseif ($task->hasChildren() > 0) {
312 $tasks[$taskcursor]['task_is_group'] = 1;
313 //$tasks[$taskcursor]['task_is_group'] = 0;
314 $tasks[$taskcursor]['task_css'] = 'ggroupblack';
315 //$tasks[$taskcursor]['task_css'] = 'gtaskblue';
316 }
317 $tasks[$taskcursor]['task_milestone'] = 0;
318 $tasks[$taskcursor]['task_percent_complete'] = $val->progress;
319 //$tasks[$taskcursor]['task_name']=$task->getNomUrl(1);
320 //print dol_print_date($val->date_start).dol_print_date($val->date_end).'<br>'."\n";
321 $tasks[$taskcursor]['task_name'] = $val->ref.' - '.$val->label;
322 $tasks[$taskcursor]['task_start_date'] = $val->date_start;
323 $tasks[$taskcursor]['task_end_date'] = $val->date_end;
324 $tasks[$taskcursor]['task_color'] = 'b4d1ea';
325
326 $idofusers = $task->getListContactId('internal');
327 $idofcontacts = $task->getListContactId('external');
328 $s = '';
329 if (count($idofusers) > 0) {
330 $s .= $langs->trans("Internals").': ';
331 $i = 0;
332 foreach ($idofusers as $valid) {
333 $userstatic->fetch($valid);
334 if ($i) {
335 $s .= ', ';
336 }
337 $s .= $userstatic->login;
338 $i++;
339 }
340 }
341 //if (count($idofusers)>0 && (count($idofcontacts)>0)) $s.=' - ';
342 if (count($idofcontacts) > 0) {
343 if ($s) {
344 $s .= ' - ';
345 }
346 $s .= $langs->trans("Externals").': ';
347 $i = 0;
348 $contactidfound = array();
349 foreach ($idofcontacts as $valid) {
350 if (empty($contactidfound[$valid])) {
351 $res = $contactstatic->fetch($valid);
352 if ($res > 0) {
353 if ($i) {
354 $s .= ', ';
355 }
356 $s .= $contactstatic->getFullName($langs);
357 $contactidfound[$valid] = 1;
358 $i++;
359 }
360 }
361 }
362 }
363
364 /* For JSGanttImproved */
365 //if ($s) $tasks[$taskcursor]['task_resources']=implode(',',$idofusers);
366 $tasks[$taskcursor]['task_resources'] = $s;
367 if ($s) {
368 $tasks[$taskcursor]['task_resources'] = '<a href="'.DOL_URL_ROOT.'/projet/tasks/contact.php?id='.$val->id.'&withproject=1" title="'.dol_escape_htmltag($s).'">'.$langs->trans("Contacts").'</a>';
369 }
370 //print "xxx".$val->id.$tasks[$taskcursor]['task_resources'];
371 $tasks[$taskcursor]['note'] = $task->note_public;
372 $taskcursor++;
373 }
374
375 // Search parent to set task_parent_alternate_id (required by ganttchart)
376 foreach ($tasks as $tmpkey => $tmptask) {
377 foreach ($tasks as $tmptask2) {
378 if ($tmptask2['task_id'] == $tmptask['task_parent']) {
379 $tasks[$tmpkey]['task_parent_alternate_id'] = $tmptask2['task_alternate_id'];
380 break;
381 }
382 }
383 if (empty($tasks[$tmpkey]['task_parent_alternate_id'])) {
384 $tasks[$tmpkey]['task_parent_alternate_id'] = $tasks[$tmpkey]['task_parent'];
385 }
386 }
387
388 print "\n";
389
390 if (!empty($conf->use_javascript_ajax)) {
391 //var_dump($_SESSION);
392
393 // How the date for data are formatted (format used bu jsgantt)
394 $dateformatinput = 'yyyy-mm-dd';
395 // How the date for data are formatted (format used by dol_print_date)
396 $dateformatinput2 = 'standard';
397 //var_dump($dateformatinput);
398 //var_dump($dateformatinput2);
399
400 $moreforfilter = '<div class="liste_titre liste_titre_bydiv centpercent">';
401
402 $moreforfilter .= '<div class="divsearchfield">';
403 //$moreforfilter .= $langs->trans("TasksAssignedTo").': ';
404 //$moreforfilter .= $form->select_dolusers($tmpuser->id > 0 ? $tmpuser->id : '', 'search_user_id', 1);
405 $moreforfilter .= '&nbsp;';
406 $moreforfilter .= '</div>';
407
408 $moreforfilter .= '</div>';
409
410 print $moreforfilter;
411
412 print '<div class="div-table-responsive">';
413
414 print '<div id="tabs" class="gantt" style="width: 80vw;">'."\n";
415 include_once DOL_DOCUMENT_ROOT.'/projet/ganttchart.inc.php';
416 print '</div>'."\n";
417
418 print '</div>';
419 } else {
420 $langs->load("admin");
421 print $langs->trans("AvailableOnlyIfJavascriptAndAjaxNotDisabled");
422 }
423} else {
424 print '<div class="opacitymedium">'.$langs->trans("NoTasks").'</div>';
425}
426
427// End of page
428llxFooter();
429$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage contact/addresses.
Class to manage generation of HTML components Only common components must be here.
Class permettant la generation de composants html autre Only common components are here.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage tasks.
Class to manage Dolibarr users.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
project_prepare_head(Project $project, $moreparam='')
Prepare array with list of tabs.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.