From af2cf0b66fa9e04bdf21fbce1a5c5c00025c6733 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur <eldy@destailleur.fr> Date: Sat, 26 Apr 2014 21:36:58 +0200 Subject: [PATCH] Fix: [ bug #1354 ] Tasks disapear in same sub-task --- htdocs/core/class/html.formother.class.php | 47 +++++--- htdocs/core/lib/functions2.lib.php | 122 +++++++++++++++++++++ htdocs/install/mysql/migration/repair.sql | 2 + htdocs/projet/class/project.class.php | 60 ---------- htdocs/projet/tasks.php | 12 +- htdocs/projet/tasks/task.php | 3 +- 6 files changed, 166 insertions(+), 80 deletions(-) diff --git a/htdocs/core/class/html.formother.class.php b/htdocs/core/class/html.formother.class.php index 3ac6ce0eba1..e4d88fab7d7 100644 --- a/htdocs/core/class/html.formother.class.php +++ b/htdocs/core/class/html.formother.class.php @@ -424,16 +424,17 @@ class FormOther /** * Return list of project and tasks * - * @param int $selectedtask Pre-selected task - * @param int $projectid Project id - * @param string $htmlname Name of html select - * @param int $modeproject 1 to restrict on projects owned by user - * @param int $modetask 1 to restrict on tasks associated to user - * @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists - * @param int $useempty 0=Allow empty values + * @param int $selectedtask Pre-selected task + * @param int $projectid Project id + * @param string $htmlname Name of html select + * @param int $modeproject 1 to restrict on projects owned by user + * @param int $modetask 1 to restrict on tasks associated to user + * @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists + * @param int $useempty 0=Allow empty values + * @param int $disablechildoftaskid 1=Disable task that are child of the provided task id * @return void */ - function selectProjectTasks($selectedtask='', $projectid=0, $htmlname='task_parent', $modeproject=0, $modetask=0, $mode=0, $useempty=0) + function selectProjectTasks($selectedtask='', $projectid=0, $htmlname='task_parent', $modeproject=0, $modetask=0, $mode=0, $useempty=0, $disablechildoftaskid=0) { global $user, $langs; @@ -448,7 +449,7 @@ class FormOther if ($useempty) print '<option value="0"> </option>'; $j=0; $level=0; - $this->_pLineSelect($j, 0, $tasksarray, $level, $selectedtask, $projectid); + $this->_pLineSelect($j, 0, $tasksarray, $level, $selectedtask, $projectid, $disablechildoftaskid); print '</select>'; } else @@ -458,17 +459,18 @@ class FormOther } /** - * Write all lines of a project (if parent = 0) + * Write lines of a project (all lines of a project if parent = 0) * * @param int &$inc Cursor counter - * @param int $parent Id parent - * @param Object $lines Line object + * @param int $parent Id of parent task we want to see + * @param array $lines Array of task lines * @param int $level Level * @param int $selectedtask Id selected task * @param int $selectedproject Id selected project + * @param int $disablechildoftaskid 1=Disable task that are child of the provided task id * @return void */ - private function _pLineSelect(&$inc, $parent, $lines, $level=0, $selectedtask=0, $selectedproject=0) + private function _pLineSelect(&$inc, $parent, $lines, $level=0, $selectedtask=0, $selectedproject=0, $disablechildoftaskid=0) { global $langs, $user, $conf; @@ -481,12 +483,12 @@ class FormOther { $var = !$var; - //var_dump($selectedtask."--".$selectedtask."--".$lines[$i]->fk_project."_".$lines[$i]->id); + //var_dump($selectedproject."--".$selectedtask."--".$lines[$i]->fk_project."_".$lines[$i]->id); // Break on a new project - if ($parent == 0) + if ($parent == 0) // We are on a task at first level { - if ($lines[$i]->fk_project != $lastprojectid) + if ($lines[$i]->fk_project != $lastprojectid) // Break found on project { if ($i > 0 && $conf->browser->firefox) print '<option value="0" disabled="disabled">----------</option>'; print '<option value="'.$lines[$i]->fk_project.'_0"'; @@ -509,11 +511,22 @@ class FormOther } } + $newdisablechildoftaskid=$disablechildoftaskid; + // Print task if ($lines[$i]->id >= 0) { + // Check if we must disable entry + $disabled=0; + if ($disablechildoftaskid && (($lines[$i]->id == $disablechildoftaskid || $lines[$i]->fk_parent == $disablechildoftaskid))) + { + $disabled++; + if ($lines[$i]->fk_parent == $disablechildoftaskid) $newdisablechildoftaskid=$lines[$i]->id; // If task is child of a disabled parent, we will propagate id to disable next child too + } + print '<option value="'.$lines[$i]->fk_project.'_'.$lines[$i]->id.'"'; if (($lines[$i]->id == $selectedtask) || ($lines[$i]->fk_project.'_'.$lines[$i]->id == $selectedtask)) print ' selected="selected"'; + if ($disabled) print ' disabled="disabled"'; print '>'; print $langs->trans("Project").' '.$lines[$i]->projectref; if (empty($lines[$i]->public)) @@ -534,7 +547,7 @@ class FormOther } $level++; - if ($lines[$i]->id) $this->_pLineSelect($inc, $lines[$i]->id, $lines, $level, $selectedtask, $selectedproject); + if ($lines[$i]->id) $this->_pLineSelect($inc, $lines[$i]->id, $lines, $level, $selectedtask, $selectedproject, $newdisablechildoftaskid); $level--; } } diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 20af2917fa4..01cb46f20f2 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -1532,3 +1532,125 @@ function dolGetElementUrl($objectid,$objecttype,$withpicto=0,$option='') } return $ret; } + + +/** + * Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child-parent loop + * + * @param string $tabletocleantree Table to clean + * @param string $fieldfkparent Field name that contains id of parent + * @return int Nb of records fixed/deleted + */ +function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent) +{ + $totalnb=0; + $listofid=array(); + $listofparentid=array(); + + // Get list of all id in array listofid and all parents in array listofparentid + $sql='SELECT rowid, '.$fieldfkparent.' as parent_id FROM '.MAIN_DB_PREFIX.$tabletocleantree; + $resql = $db->query($sql); + if ($resql) + { + $num = $db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $db->fetch_object($resql); + $listofid[]=$obj->rowid; + if ($obj->parent_id > 0) $listofparentid[$obj->rowid]=$obj->parent_id; + $i++; + } + } + else + { + dol_print_error($db); + } + + if (count($listofid)) + { + print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n"; + + // Check loops on each other + $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself + dol_syslog("sql=".$sql); + $resql = $db->query($sql); + if ($resql) + { + $nb=$db->affected_rows($sql); + if ($nb > 0) + { + print '<br>Some record that were parent of themself were cleaned.'; + } + + $totalnb+=$nb; + } + //else dol_print_error($db); + + // Check other loops + $listofidtoclean=array(); + foreach($listofparentid as $id => $pid) + { + // Check depth + //print 'Analyse record id='.$id.' with parent '.$pid.'<br>'; + + $cursor=$id; $arrayidparsed=array(); // We start from child $id + while ($cursor > 0) + { + $arrayidparsed[$cursor]=1; + if ($arrayidparsed[$listofparentid[$cursor]]) // We detect a loop. A record with a parent that was already into child + { + print 'Found a loop between id '.$id.' - '.$cursor.'<br>'; + unset($arrayidparsed); + $listofidtoclean[$cursor]=$id; + break; + } + $cursor=$listofparentid[$cursor]; + } + + if (count($listofidtoclean)) break; + } + + $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree; + $sql.= " SET ".$fieldfkparent." = 0"; + $sql.= " WHERE rowid IN (".join(',',$listofidtoclean).")"; // So we update only records detected wrong + dol_syslog("sql=".$sql); + $resql = $db->query($sql); + if ($resql) + { + $nb=$db->affected_rows($sql); + if ($nb > 0) + { + // Removed orphelins records + print '<br>Some records were detected to have parent that is a child, we set them as root record for id: '; + print join(',',$listofidtoclean); + } + + $totalnb+=$nb; + } + //else dol_print_error($db); + + // Check and clean orphelins + $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree; + $sql.= " SET ".$fieldfkparent." = 0"; + $sql.= " WHERE ".$fieldfkparent." NOT IN (".join(',',$listofid).")"; // So we update only records linked to a non existing parent + dol_syslog("sql=".$sql); + $resql = $db->query($sql); + if ($resql) + { + $nb=$db->affected_rows($sql); + if ($nb > 0) + { + // Removed orphelins records + print '<br>Some orphelins were found and modified to be parent so records are visible again for id: '; + print join(',',$listofid); + } + + $totalnb+=$nb; + } + //else dol_print_error($db); + + print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.'; + return $totalnb; + } +} diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql index 7a46fbd48ed..ca2fbf78997 100644 --- a/htdocs/install/mysql/migration/repair.sql +++ b/htdocs/install/mysql/migration/repair.sql @@ -142,3 +142,5 @@ update llx_societe set barcode = null where (rowid, barcode) in (select max_rowi drop table tmp_societe_double; +UPDATE llx_projet_task SET fk_task_parent = 0 WHERE fk_task_parent = rowid + diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 719c7c5f034..277b241f259 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -1243,66 +1243,6 @@ class Project extends CommonObject return $result; } - /** - * Clean tasks not linked to an existing parent - * - * @return int Nb of records deleted - */ - function clean_orphelins() - { - $nb=0; - - // There is orphelins. We clean that - $listofid=array(); - - // Get list of all id in array listofid - $sql='SELECT rowid FROM '.MAIN_DB_PREFIX.'projet_task'; - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num && $i < 100) - { - $obj = $this->db->fetch_object($resql); - $listofid[]=$obj->rowid; - $i++; - } - } - else - { - dol_print_error($this->db); - } - - if (count($listofid)) - { - print 'Code asked to check and clean orphelins.'; - - $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task"; - $sql.= " SET fk_task_parent = 0"; - $sql.= " WHERE fk_task_parent NOT IN (".join(',',$listofid).")"; // So we update only records linked to a non existing parent - - $resql = $this->db->query($sql); - if ($resql) - { - $nb=$this->db->affected_rows($sql); - - if ($nb > 0) - { - // Removed orphelins records - print 'Some orphelins were found and modified to be parent so records are visible again: '; - print join(',',$listofid); - } - - return $nb; - } - else - { - return -1; - } - } - } - /** * Associate element to a project diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php index 34dc9232e81..36792d4d0ad 100644 --- a/htdocs/projet/tasks.php +++ b/htdocs/projet/tasks.php @@ -438,11 +438,19 @@ else { if ($mode=='mine') { - if ($nboftaskshown < count($tasksrole)) $object->clean_orphelins(); + if ($nboftaskshown < count($tasksrole)) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + cleanCorruptedTree($db, 'projet_task', 'fk_task_parent'); + } } else { - if ($nboftaskshown < count($tasksarray)) $object->clean_orphelins(); + if ($nboftaskshown < count($tasksarray)) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + cleanCorruptedTree($db, 'projet_task', 'fk_task_parent'); + } } } } diff --git a/htdocs/projet/tasks/task.php b/htdocs/projet/tasks/task.php index 60fdbda2c70..cab79eb7326 100644 --- a/htdocs/projet/tasks/task.php +++ b/htdocs/projet/tasks/task.php @@ -317,7 +317,8 @@ if ($id > 0 || ! empty($ref)) // Task parent print '<tr><td>'.$langs->trans("ChildOfTask").'</td><td>'; - print $formother->selectProjectTasks($object->fk_task_parent,$projectstatic->id, 'task_parent', $user->admin?0:1, 0); +// print $formother->selectProjectTasks($object->fk_task_parent, $projectstatic->id, 'task_parent', ($user->admin?0:1), 0, 0, 0, $object->id); + print $formother->selectProjectTasks($object->fk_task_parent, $projectstatic->id, 'task_parent', ($user->admin?0:1), 0, 0, 0, 0); print '</td></tr>'; // Date start -- GitLab