tr('Gantt Chart'), 'description' => tr('Create and display a gantt graphic using tracker data'), 'prefs' => ['wikiplugin_ganttchart'], 'tags' => ['experimental'], 'introduced' => 19, 'format' => 'html', 'iconname' => 'table', 'params' => [ 'trackerId' => [ 'name' => tr('Tracker ID'), 'description' => tr('Tracker to search from'), 'required' => true, 'default' => 0, 'filter' => 'int', 'profile_reference' => 'tracker', 'since' => 19, ], 'order' => [ 'name' => tr('Order Field'), 'description' => tr('Permanent name of the field to use for row number order'), 'required' => true, 'filter' => 'word', 'since' => 19, ], 'level' => [ 'name' => tr('Level Field'), 'description' => tr('Permanent name of the field to use for row level'), 'required' => true, 'filter' => 'word', 'since' => 19, ], 'status' => [ 'name' => tr('Status Field'), 'description' => tr('Permanent name of the field to use for row status'), 'required' => true, 'filter' => 'word', 'since' => 19, ], 'depends' => [ 'name' => tr('Dependency Field'), 'description' => tr('Permanent name of the field to use for row dependency'), 'required' => true, 'filter' => 'word', 'since' => 19, ], 'progress' => [ 'name' => tr('Progress Field'), 'description' => tr('Permanent name of the field to use for row progress, values between 0-100'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'name' => [ 'name' => tr('Name Field'), 'description' => tr('Permanent name of the field to use for row name'), 'required' => true, 'filter' => 'word', 'since' => 19, ], 'description' => [ 'name' => tr('Description Field'), 'description' => tr('Permanent name of the field to use for row description'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'code' => [ 'name' => tr('Code Field'), 'description' => tr('Permanent name of the field to use for row code'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'begin' => [ 'name' => tr('Begin Date Field'), 'description' => tr('Permanent name of the field to use for event beginning'), 'required' => true, ], 'end' => [ 'name' => tr('End Date Field'), 'description' => tr('Permanent name of the field to use for event ending'), 'required' => true, 'since' => 19, ], 'resourceId' => [ 'name' => tr('Resources Field'), 'description' => tr('Permanent name of the field to use for resources ending'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'roleId' => [ 'name' => tr('Roles Field'), 'description' => tr('Permanent name of the field to use for roles ending'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'effort' => [ 'name' => tr('Effort Field'), 'description' => tr('Permanent name of the field to use for effort ending, values in milliseconds'), 'required' => false, 'filter' => 'word', 'since' => 19, ], 'startIsMilestone' => [ 'name' => tr('Start Is Milestone'), 'description' => tr('Flag field to start is milestone'), 'required' => false, 'filter' => 'none', 'since' => 19, ], 'endIsMilestone' => [ 'name' => tr('End Is Milestone'), 'description' => tr('Flag field to end is milestone'), 'required' => false, 'filter' => 'none', 'since' => 19, ], 'canWrite' => [ 'name' => tr('Writable'), 'description' => tr('Flag field to write or not in tasks'), 'required' => false, 'filter' => 'none', 'since' => 19, ], 'canDelete' => [ 'name' => tr('Deletable'), 'description' => tr('Flag field that permit to delete or not tasks'), 'required' => false, 'filter' => 'none', 'since' => 19, ], 'canWriteOnParent' => [ 'name' => tr('Parent Writable'), 'description' => tr('Flag field to write or not in parent tasks'), 'required' => false, 'filter' => 'none', 'since' => 19, ], 'canDuplicate' => [ 'name' => tr('Allow gantt items duplication'), 'description' => tr('Flag field to allow duplicate tasks'), 'required' => false, 'filter' => 'alpha', 'default' => 'y', 'options' => [ ['text' => tra('Yes'), 'value' => 'y'], ['text' => tra('No'), 'value' => 'n'] ], 'since' => 23, ], 'ganttId' => [ 'name' => tr('Gantt chart ID'), 'description' => tr('Gantt chart ID value to filter'), 'required' => false, 'filter' => 'word', 'since' => 23, ], 'ganttIdField' => [ 'name' => tr('Gantt chart field permanent name'), 'description' => tr('Permanent name of the field to use to filters the ganttId'), 'required' => false, 'filter' => 'word', 'since' => 23, ], ], ]; } function wikiplugin_ganttchart($data, $params) { //checking if vendor files are present if (! file_exists('vendor_bundled/vendor/robicch/jquery-gantt')) { return WikiParser_PluginOutput::internalError( tr( 'Missing required files, please make sure plugin files are installed at vendor_bundled/vendor/robicch/jquery-gantt.

To install, please run composer.' ) ); } $access = TikiLib::lib('access'); $access->setTicket(); $trackerId = $params['trackerId']; $order = ! empty($params['order']) ? $params['order'] : ''; $levelParam = ! empty($params['level']) ? $params['level'] : ''; $status = $params['status']; $depends = ! empty($params['depends']) ? $params['depends'] : ''; $progress = ! empty($params['progress']) ? $params['progress'] : ''; $name = $params['name']; $description = $params['description']; $code = $params['code']; $begin = $params['begin']; $end = $params['end']; $resources = ! empty($params['resourceId']) ? $params['resourceId'] : ''; $roles = ! empty($params['roleId']) ? $params['roleId'] : ''; $effort = ! empty($params['effort']) ? $params['effort'] : ''; $startIsMilestone = ! empty($params['startIsMilestone']) ? $params['startIsMilestone'] : ''; $endIsMilestone = ! empty($params['endIsMilestone']) ? $params['endIsMilestone'] : ''; $canWrite = ! empty($params['canWrite']) ? filter_var($params['canWrite'], FILTER_VALIDATE_BOOLEAN) : false; $canDelete = ! empty($params['canDelete']) ? filter_var($params['canDelete'], FILTER_VALIDATE_BOOLEAN) : false; $canWriteOnParent = ! empty($params['canWriteOnParent']) ? filter_var($params['canWriteOnParent'], FILTER_VALIDATE_BOOLEAN) : false; $ganttId = ! empty($params['ganttId']) ? $params['ganttId'] : ''; $ganttIdField = ! empty($params['ganttIdField']) ? $params['ganttIdField'] : ''; $definition = Tracker_Definition::get($trackerId); if (! $definition) { return WikiParser_PluginOutput::userError(tr('Tracker data source not found.')); } if (empty($order)) { return WikiParser_PluginOutput::userError(tr('Order tracker field parameter is mandatory.')); } if (empty($levelParam)) { return WikiParser_PluginOutput::userError(tr('Level tracker field parameter is mandatory.')); } if (empty($depends)) { return WikiParser_PluginOutput::userError(tr('Depends tracker field parameter is mandatory.')); } $trklib = TikiLib::lib('trk'); $ganttIdFieldDef = $ganttIdField ? $trklib->get_tracker_field($ganttIdField) : null; if (! empty($ganttIdField) && ! $ganttIdFieldDef) { return WikiParser_PluginOutput::userError(tr('ganttIdField parameter is not valid field perm_name.')); } $trackerDefinition = Tracker_Definition::get($trackerId); $listfields = $trackerDefinition->getFields(); $orderField = $trklib->get_tracker_field($order); $orderItems = ! empty($orderField['fieldId']) ? 'f_' . $orderField['fieldId'] . '_asc' : 'created_asc'; $filterField = $ganttIdFieldDef ? [$ganttIdFieldDef['fieldId']] : ''; $filterExactValue = $ganttIdFieldDef ? [$ganttId] : ''; $listItems = $trklib->list_items($trackerId, 0, -1, $orderItems, $listfields, $filterField, '', '', '', $filterExactValue); $ganttValues = []; $ganttRoles = []; $allResources = []; $allRoles = []; $filterRoles = []; $allFilterRoles = []; $allLevel = []; $listHasChildren = []; //Get all users $userlib = TikiLib::lib('user'); $users = $userlib->list_all_users(); //fetch user to show in select foreach ($users as $key => $value) { $allResources[] = [ 'id' => $value, 'name' => $value, ]; } $itemIds = []; $roleId = 1; foreach ($listItems['data'] as $item) { $fieldId = ''; $fieldItemValues = $item['field_values']; $itemId = $item['itemId']; $fieldOrder = 0; $fieldStatus = ''; $fieldDepends = ''; $fieldName = ''; $fieldProgress = 0; $fieldDescription = ''; $fieldCode = ''; $fieldStartDate = ''; $fieldEndDate = ''; $selectedResourceValue = ''; $selectedRoleValue = ''; $fieldEffort = 0; $fieldStartIsMilestone = false; $fieldEndIsMilestone = false; $hasChild = false; foreach ($fieldItemValues as $fieldItem) { if ($ganttIdFieldDef && $fieldItem['fieldId'] == $ganttIdFieldDef['fieldId']) { $fieldId = $fieldItem['value']; } if (! isset($allLevel[$fieldItem['itemId']])) { $allLevel[$fieldItem['itemId']] = 0; } if ($levelParam == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $level = isset($allLevel[$fieldItem['value']]) ? $allLevel[$fieldItem['value']] : 0; $level++; $allLevel[$fieldItem['itemId']] = $level; $listHasChildren[$fieldItem['value']] = true; } if ($order == $fieldItem['permName'] && is_numeric($fieldItem['value'])) { $fieldOrder = (int)$fieldItem['value']; } if ($status == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldStatus = $fieldItem['value']; } if ($depends == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldDepends = $fieldItem['value']; } if ($progress == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldProgress = $fieldItem['value']; } if ($name == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldName = $fieldItem['value']; } if ($description == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldDescription = $fieldItem['value']; } if ($code == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldCode = $fieldItem['value']; } if ($begin == $fieldItem['permName'] && is_numeric($fieldItem['value'])) { $fieldStartDate = ($fieldItem['value'] * 1000); } if ($end == $fieldItem['permName'] && is_numeric($fieldItem['value'])) { $fieldEndDate = ($fieldItem['value'] * 1000); } if ($resources == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $selectedResourceValue = $fieldItem['value']; } if ($roles == $fieldItem['permName'] && ! empty($fieldItem['value'])) { if (! in_array($fieldItem['value'], $ganttRoles)) { $ganttRoles[] = $fieldItem['value']; $allRoles[] = [ 'id' => 'role_' . $roleId, 'name' => $fieldItem['value'] ]; } $selectedRoleValue = $fieldItem['value']; $roleId++; } if ($effort == $fieldItem['permName'] && ! empty($fieldItem['value'])) { $fieldEffort = $fieldItem['value']; } if ($startIsMilestone == $fieldItem['permName'] && $fieldItem['value'] == 'y') { $fieldStartIsMilestone = true; } if ($endIsMilestone == $fieldItem['permName'] && $fieldItem['value'] == 'y') { $fieldEndIsMilestone = true; } } $itemIds[] = $itemId; $numDays = countWorkingDays(($fieldStartDate / 1000), ($fieldEndDate / 1000)); $resourceIdValue = ''; foreach ($allResources as $resource) { if ($resource['name'] == $selectedResourceValue) { $resourceIdValue = $resource['id']; } } $roleIdValue = ''; foreach ($allRoles as $role) { if ($role['name'] == $selectedRoleValue) { $roleIdValue = $role['id']; if ($ganttIdFieldDef && ! in_array($role['name'], $filterRoles)) { $allFilterRoles[] = [ 'id' => $role['id'], 'name' => $role['name'] ]; $filterRoles[] = $role['name']; } } } $ganttValues[] = [ 'id' => $itemId, 'fieldId' => $fieldId, 'name' => $fieldName, 'order' => $fieldOrder, 'progress' => $fieldProgress, 'progressByWorklog' => false, 'relevance' => 0, 'type' => '', 'typeId' => '', 'description' => $fieldDescription, 'code' => $fieldCode, 'level' => $allLevel[$itemId], 'status' => $fieldStatus, 'depends' => $fieldDepends, 'canWrite' => $canWrite, 'start' => $fieldStartDate, 'duration' => $numDays, 'end' => $fieldEndDate, 'startIsMilestone' => $fieldStartIsMilestone, 'endIsMilestone' => $fieldEndIsMilestone, 'collapsed' => false, 'assigs' => [ 0 => [ 'resourceId' => $resourceIdValue, 'id' => uniqid(), 'roleId' => $roleIdValue, 'effort' => (int)$fieldEffort ] ], 'hasChild' => $hasChild ]; } $ganttValues = checkChildrens($ganttValues, $listHasChildren); $ganttValues = transformDependenciesIdsToIndex($ganttValues, $itemIds); $info = ! empty($_POST) ? $_POST : []; if (! empty($allFilterRoles)) { $allRoles = $allFilterRoles; } updateTasks($info, $params, $allResources, $allRoles); $canDuplicate = ($params['canDuplicate'] ?? 'y') === 'y'; if (isset($info['ganttDuplicator']) && $canDuplicate) { duplicate($info, $params, $listItems['data'], $levelParam, $depends); } else { save($info, $params, $allResources, $allRoles, true); } $headerlib = TikiLib::lib('header'); $headerlib->add_cssfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/dateField/jquery.dateField.css'); $headerlib->add_cssfile('vendor_bundled/vendor/robicch/jquery-gantt/gantt.css'); $headerlib->add_cssfile('themes/base_files/feature_css/wikiplugin-ganttchart.css'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/jquery.livequery.1.1.1.min.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/jquery.timers.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/utilities.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/forms.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/date.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/dialogs.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/layout.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/i18nJs.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/dateField/jquery.dateField.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/JST/jquery.JST.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/libs/jquery/svg/jquery.svg.min.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttUtilities.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttTask.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttDrawerSVG.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttZoom.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttGridEditor.js'); $headerlib->add_jsfile('vendor_bundled/vendor/robicch/jquery-gantt/ganttMaster.js'); $headerlib->add_jsfile('lib/jquery_tiki/wikiplugin-ganttchart.js', true); $smarty = TikiLib::lib('smarty'); $smarty->assign('trackerId', $trackerId); $permsTracker = TikiLib::lib('tiki')->get_perm_object($trackerId, 'tracker'); if ($permsTracker['tiki_p_create_tracker_items'] === 'y' && $canDuplicate) { $smarty->assign('ganttId', $ganttId); $smarty->assign('ganttIdField', $ganttIdField); } $ganttProject = [ 'tasks' => $ganttValues, 'deletedTaskIds' => [], 'resources' => $allResources, 'roles' => $allRoles, 'canWrite' => $canWrite, 'canDelete' => $canDelete, 'canWriteOnParent' => $canWriteOnParent, 'zoom' => "1M" ]; $smarty->assign('ganttProject', json_encode($ganttProject)); return $smarty->fetch('wiki-plugins/wikiplugin_ganttchart.tpl'); } /** * Count working days between two dates * * @param int $startDate * @param int $endDate * @return int */ function countWorkingDays($startDate, $endDate) { $endDate = ! is_numeric($endDate) ? strtotime($endDate) : $endDate; $startDate = ! is_numeric($startDate) ? strtotime($startDate) : $startDate; $days = ($endDate - $startDate) / 86400 + 1; $no_full_weeks = floor($days / 7); $no_remaining_days = fmod($days, 7); $the_first_day_of_week = date("N", $startDate); $the_last_day_of_week = date("N", $endDate); if ($the_first_day_of_week <= $the_last_day_of_week) { if ($the_first_day_of_week <= 6 && 6 <= $the_last_day_of_week) { $no_remaining_days--; } if ($the_first_day_of_week <= 7 && 7 <= $the_last_day_of_week) { $no_remaining_days--; } } else { if ($the_first_day_of_week == 7) { $no_remaining_days--; if ($the_last_day_of_week == 6) { $no_remaining_days--; } } else { $no_remaining_days -= 2; } } $workingDays = $no_full_weeks * 5; if ($no_remaining_days > 0) { $workingDays += $no_remaining_days; } return (int)$workingDays; } /** * Convert time into milliseconds * * @param string $time * @return int */ function getTimeToMilliseconds($time) { $time = ! empty($time) ? explode(":", $time) : ''; $milisec = 0; if (isset($time[0]) && isset($time[1])) { $hour = (int)$time[0] * 3600000; $minutes = (int)$time[1] * 60000; $milisec = $hour + $minutes; } return $milisec; } /** * Update gantt children values * * @param array $ganttValues * @param array $listHasChildren * @return array */ function checkChildrens($ganttValues, $listHasChildren) { if (! empty($ganttValues) && ! empty($listHasChildren)) { foreach ($ganttValues as $key => $ganttItem) { if (isset($listHasChildren[$ganttItem['id']])) { $ganttValues[$key]['hasChild'] = true; } } } return $ganttValues; } /** * Update tracker item information * * @param array $info * @param array $params * @param array $allResources * @param array $allRoles * @param boolean $notifications * @return null */ function save($info, $params, $allResources, $allRoles, $notifications = false) { $access = TikiLib::lib('access'); if (! empty($info) && $access->checkCsrf()) { $trackerId = ! empty($info['trackerId']) ? $info['trackerId'] : 0; $trackerItemId = ! empty($info['trackerItemId']) ? $info['trackerItemId'] : 0; $info['resourceId'] = isset($info['resourceId']) ? $info['resourceId'] : ''; $info['roleId'] = isset($info['roleId']) ? $info['roleId'] : ''; $info['effort'] = isset($info['effort']) ? $info['effort'] : ''; if ($trackerId != $params['trackerId']) { Feedback::error(tr('You are trying to update a tracker that is not used by this gantt chart')); } else { $fields = []; foreach ($info as $key => $field) { $fieldPermanentName = ! empty($params[$key]) ? $params[$key] : null; if (is_string($fieldPermanentName)) { $value = $field; if (in_array($key, ['begin', 'end'])) { if (strlen($field) > 10) { $value = substr($field, 0, -3); } else { $value = strtotime($field); } } if (in_array($key, ['resourceId', 'roleId'])) { foreach ($allResources as $resource) { if ($resource['id'] == $field) { $value = $resource['name']; } } foreach ($allRoles as $role) { if ($role['id'] == $field) { $value = $role['name']; } } } if ($key === 'effort') { $value = getTimeToMilliseconds($field); } $fields[$fieldPermanentName] = $value; } } $item = [ 'itemId' => $trackerItemId, 'fields' => $fields ]; $definition = Tracker_Definition::get($trackerId); $trackerUtilities = new Services_Tracker_Utilities(); $result = $trackerUtilities->updateItem($definition, $item); if ($result && $notifications) { Feedback::success(tr('Gantt chart updated')); $access->redirect($_SERVER['HTTP_REFERER']); } } } } /** * Duplicate tracker item information * * @param array $info * @param array $params * @param array $trackerData * @param string $levelParam * @param string $depends * @return null */ function duplicate(array $info, array $params, array $trackerData, string $levelParam = '', string $depends = '') { $access = TikiLib::lib('access'); if (! empty($info) && $access->checkCsrf()) { $error = false; $trackerId = ! empty($info['ganttId']) ? $info['trackerId'] : 0; $sourceGanttId = ! empty($info['sourceGanttId']) ? trim($info['sourceGanttId']) : ''; $ganttId = ! empty($info['ganttId']) ? trim($info['ganttId']) : ''; $ganttIdField = ! empty($info['ganttIdField']) ? trim($info['ganttIdField']) : ''; if ($trackerId != $params['trackerId']) { Feedback::error(tr('You are trying to update a tracker that is not used by this gantt chart')); $error = true; } $definition = Tracker_Definition::get($trackerId); $permsTracker = TikiLib::lib('tiki')->get_perm_object($trackerId, 'tracker', $definition->getInformation()); if ($permsTracker['tiki_p_create_tracker_items'] === 'n') { Feedback::error(tr('You do not have permission to create tracker items')); $error = true; } $definition = Tracker_Definition::get($trackerId); if (! $definition) { Feedback::error(tr('Tracker definitions not found')); $error = true; } if (empty($ganttIdField)) { Feedback::error(tr('Gantt Field ID is not defined')); $error = true; } if (empty($ganttId)) { Feedback::error(tr('Gantt ID cannot be empty')); $error = true; } $trackerlib = TikiLib::lib('trk'); $trackerField = $trackerlib->get_tracker_field($ganttIdField); // CHECK IF CHOSEN GANTT ID EXISTS $items = $trackerlib->list_items($trackerId, 0, -1, '', '', [$trackerField['fieldId']], '', '', '', [$ganttId]); if ($items['cant']) { Feedback::error(tr('Gantt ID already exists')); $error = true; } if (empty($trackerData)) { Feedback::error(tr('Tracker items data not found')); $error = true; } if ($error) { $access->redirect($_SERVER['HTTP_REFERER']); } $trackerUtilities = new Services_Tracker_Utilities(); $countDuplicatedItems = 0; $insertedItems = []; $mapFields = []; $mapLevel = []; $mapDependencies = []; foreach ($trackerData as $data) { $itemFields = []; $itemFields['status'] = $data['status'] ?? null; $itemFields['fields'] = []; $fieldValues = ! empty($data['field_values']) ? $data['field_values'] : []; foreach ($fieldValues as $field) { $permName = ! empty($field['permName']) ? $field['permName'] : ''; $value = $field['value'] ?? ''; if (! empty($value) && $permName == $levelParam) { $mapLevel[$data['itemId']] = $value; } if (! empty($value) && $permName == $depends) { $mapDependencies[$data['itemId']] = $value; } if ($permName == $ganttIdField && $value == $sourceGanttId) { $value = $ganttId; } if (! empty($permName)) { $itemFields['fields'][$field['permName']] = $value; } } $itemId = $trackerUtilities->insertItem($definition, $itemFields); $itemFields['itemId'] = $itemId; $insertedItems[] = $itemFields; $mapFields[$data['itemId']] = $itemId; if ($itemId) { $countDuplicatedItems++; } } // Map levels and dependencies if (! empty($mapLevel) || ! empty($mapDependencies)) { foreach ($insertedItems as $items) { $fields = $items['fields'] ?? []; foreach ($fields as $permName => $value) { if (! $value && isset($mapFields[$value]) && ($permName == $levelParam || $permName == $depends)) { $items['fields'][$permName] = $mapFields[$value]; } } $trackerUtilities->updateItem($definition, $items); } } if ($success = $countDuplicatedItems == count($trackerData)) { Feedback::success(tr('Gantt chart duplicated')); } $page = ! empty($GLOBALS['page']) ? $GLOBALS['page'] : ($_REQUEST['page'] ?? null); if ($success && isset($info['updateToDuplicate']) && isset($page)) { $tikilib = TikiLib::lib('tiki'); $pageInfo = $tikilib->get_page_info($page); $pageData = ! empty($pageInfo['data']) ? $pageInfo['data'] : ''; $permsPage = $tikilib->get_perm_object($page, 'wiki page', $pageData, false); $tikiPEdit = ! empty($permsPage['tiki_p_edit']) ? $permsPage['tiki_p_edit'] : 'n'; if ($tikiPEdit === 'y') { try { $util = new Services_Edit_Utilities(); $util->replacePlugin(new JitFilter([ 'page' => $page, 'message' => tr('Gantt chart plugin updated'), 'type' => 'ganttchart', 'content' => '~same~', 'index' => 1, 'appendParams' => 1, 'params' => [ 'ganttId' => $ganttId, ] ]), false); } catch (Exception $e) { Feedback::error($e->getMessage()); } } } $access->redirect($_SERVER['HTTP_REFERER']); } } /** * Transform all tasks dependencies ids in to indexes. This is needed to ganttChart. * * @param array $ganttValues * @param array $itemIds * @return array */ function transformDependenciesIdsToIndex($ganttValues, $itemIds) { foreach ($ganttValues as $key => $value) { if (! isset($value['depends']) || strlen($value['depends']) == 0) { continue; } $depends = explode(',', $value['depends']); if (is_array($depends)) { $indexIds = []; foreach ($depends as $itemId) { $itemData = explode(':', $itemId); $itemId = $itemData[0]; $itemDays = isset($itemData[1]) ? $itemData[1] : false; $index = array_search($itemId, $itemIds); if ($index) { $index = $index + 1; $index = ! empty($itemDays) ? $index . ':' . $itemDays : $index; $indexIds[] = $index; } } $depends = ! empty($indexIds) ? implode(',', $indexIds) : ''; } $ganttValues[$key]['depends'] = $depends; } return $ganttValues; } /** * Transform all tasks dependencies indexes to ids. * * @param string $depends * @param array $tasks * @return string */ function transformDependenciesIndexToIds($depends, $tasks) { $dependsIds = ''; if (! empty($depends) && ! empty($tasks)) { $indexes = explode(',', $depends); foreach ($indexes as $value) { $itemData = explode(':', $value); $index = $itemData[0]; $index = isset($tasks[$index - 1]['id']) ? $tasks[$index - 1]['id'] : false; $itemDays = isset($itemData[1]) ? $itemData[1] : false; if ($index && $itemDays) { $index = $index . ':' . $itemDays; } $dependsIds .= ($index == '') ? $index : ','.$index; } } return $dependsIds; } /** * Update/delete gantt tasks in tracker items * * @param array $info * @return null * @throws Services_Exception */ function updateTasks($info, $params, $allResources, $allRoles) { $access = TikiLib::lib('access'); if ( ! empty($info) && $access->checkCsrf() && ! empty($info['trackerId']) && (! empty($info['deletedIds']) || ! empty($info['tasks'])) ) { $tikilib = TikiLib::lib('tiki'); $trklib = TikiLib::lib('trk'); $transaction = $tikilib->begin(); foreach ($info['deletedIds'] as $deletedId) { $itemInfo = $trklib->get_item_info($deletedId); $actionObject = Tracker_Item::fromInfo($itemInfo); if ($actionObject->canRemove()) { $trklib->remove_tracker_item($deletedId); } } $transaction->commit(); $order = 1; foreach ($info['tasks'] as $key => $task) { // mapping gantt task to tracker items $task['trackerId'] = $info['trackerId']; $task['trackerItemId'] = $task['id']; $task['begin'] = $task['start']; $task['order'] = $order; $task['resourceId'] = ! empty($task['assigs'][0]['resourceId']) ? $task['assigs'][0]['resourceId'] : ''; $task['roleId'] = ! empty($task['assigs'][0]['roleId']) ? $task['assigs'][0]['roleId'] : ''; $task['effort'] = ! empty($task['assigs'][0]['effort']) ? $task['assigs'][0]['effort'] : ''; $level = ! empty($task['level']) ? $task['level'] : 0; $task['level'] = getTaskLevel($level, $info['tasks']); if (! empty($task['depends'])) { $task['depends'] = transformDependenciesIndexToIds($task['depends'], $info['tasks']); } unset($task['id']); unset($task['start']); unset($task['assigs']); save($task, $params, $allResources, $allRoles); $order++; } Feedback::success(tr('Gantt chart updated')); exit; } } /** * Get tasks level based in gantt identation * * @param array $taskLevel * @param array $allTasks * @return int */ function getTaskLevel($taskLevel, $allTasks) { $level = 0; if (! empty($taskLevel) && $taskLevel > 0 && ! empty($allTasks)) { foreach ($allTasks as $key => $task) { $matchKey = (int)$taskLevel - 1; if ($matchKey == $key && isset($task['id'])) { $level = $task['id']; break; } } } return $level; }