public function toSelect($returnClassedHTML = false, $includeSkillGaps = false, $showQuestions = false, $iterator = false, $lessons = false, $courses = false) { if (!$iterator) { $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->tree), RecursiveIteratorIterator::SELF_FIRST)); } if ($lessons === false) { //If a lessons list is not specified, get all active lessons if ($showQuestions) { $result = eF_getTableData("lessons l JOIN questions q ON q.lessons_ID = l.id", "l.id, l.name, count(q.id) as questions", "q.type <> 'raw_text' AND l.archive = 0 AND l.active=1", "", "l.name"); } else { $result = eF_getTableData("lessons", "*", "archive = 0 && active=1"); //Get all lessons at once, thus avoiding looping queries } foreach ($result as $value) { $value['name'] = str_replace("'", "'", $value['name']); $lessons[$value['id']] = new EfrontLesson($value); //Create an array of EfrontLesson objects } } $directionsQuestions = array(); $directionsLessons = array(); foreach ($lessons as $id => $lesson) { if (!$lesson->lesson['active']) { //Remove inactive lessons unset($lessons[$id]); } elseif (!$lesson->lesson['course_only']) { //Lessons in courses will be handled by the course's display method, so remove them from the list $directions_ID = $lesson->lesson['directions_ID']; $directionsLessons[$directions_ID][] = $id; //Create an intermediate array that maps lessons to directions if ($showQuestions) { if (isset($directionsQuestions[$directions_ID])) { $directionsQuestions[$directions_ID] += $lesson->lesson['questions']; } else { $directionsQuestions[$directions_ID] = $lesson->lesson['questions']; } } } } if ($courses === false) { //If a courses list is not specified, get all active courses if ($showQuestions) { $resultQuestions = eF_getTableData("courses c JOIN lessons_to_courses lc ON lc.courses_ID = c.id JOIN questions q ON q.lessons_ID = lc.lessons_ID", "c.id, count(q.id) as questions", "q.type <> 'raw_text' AND c.archive = 0 AND c.active=1", "", "c.name"); $coursesQuestions = array(); foreach ($resultQuestions as $resultQs) { $coursesQuestions[$resultQs['id']] = $resultQs['questions']; } } $result = eF_getTableData("courses", "*", "archive = 0 AND active=1"); //Get all courses at once, thus avoiding looping queries foreach ($result as $value) { $value['name'] = str_replace("'", "'", $value['name']); $value['questions'] = $coursesQuestions[$value['id']] != "" ? $coursesQuestions[$value['id']] : 0; // 0 + to cast empty values to 0 $courses[$value['id']] = new EfrontCourse($value); //Create an array of EfrontCourse objects } } $directionsCourses = array(); foreach ($courses as $id => $course) { if (!$course->course['active']) { //Remove inactive courses unset($courses[$id]); } else { $directions_ID = $course->course['directions_ID']; $directionsCourses[$directions_ID][] = $id; //Create an intermediate array that maps courses to directions if ($showQuestions) { if (isset($directionsQuestions[$directions_ID])) { $directionsQuestions[$directions_ID] += $course->course['questions']; } else { $directionsQuestions[$directions_ID] = $course->course['questions']; } } } } //We need to calculate which directions will be displayed. We will keep only directions that have lessons or courses and their parents. In order to do so, we traverse the directions tree and set the 'hasNodes' attribute to the nodes that will be kept foreach ($iterator as $key => $value) { if (isset($directionsLessons[$value['id']]) || isset($directionsCourses[$value['id']])) { $count = $iterator->getDepth(); $value['hasNodes'] = true; isset($directionsLessons[$value['id']]) ? $value['lessons'] = $directionsLessons[$value['id']] : null; //Assign lessons ids to the direction isset($directionsCourses[$value['id']]) ? $value['courses'] = $directionsCourses[$value['id']] : null; //Assign courses ids to the direction while ($count) { $node = $iterator->getSubIterator($count--); $node['hasNodes'] = true; //Mark "keep" all the parents of the node } } } $iterator = new EfrontNodeFilterIterator($iterator, array('hasNodes' => true)); //Filter in only tree nodes that have the 'hasNodes' attribute $iterator->rewind(); $current = $iterator->current(); // pr($current); $current_level_father = 0; $treeArray = array(); if ($includeSkillGaps) { $treeArray['lesson_0'] = _SKILLGAPTESTS; } $offset = ""; while ($iterator->valid()) { $children = array(); //The $children array is used so that when collapsing a direction, all its children disappear as well foreach (new EfrontNodeFilterIterator(new ArrayIterator($this->getNodeChildren($current), RecursiveIteratorIterator::SELF_FIRST)) as $key => $value) { $children[] = $key; } if ($offset != "") { $treeArray['direction_' . $current['id']] = str_replace("'", "'", $offset . " " . $current['name']); } else { $treeArray['direction_' . $current['id']] = str_replace("'", "'", $current['name']); } if (sizeof($current['lessons']) > 0) { foreach ($current->offsetGet('lessons') as $lessonId) { $treeArray['lesson_' . $current['id'] . '_' . $lessonId] = str_replace("'", "'", $offset . "- " . $lessons[$lessonId]->lesson['name']); } } if (sizeof($current['courses']) > 0) { foreach ($current->offsetGet('courses') as $courseId) { $coursesArray = $courses[$courseId]->toSelect(); $first = 1; foreach ($coursesArray as $courseId => $courseName) { // The first result is the name of the course - the rest lesson names // We need this distinction to have different keys (starting with course_ or lesson_ correctly if ($first) { $treeArray['course_' . $current['id'] . '_' . $courseId . "_" . $courseId] = str_replace("'", "'", $offset . "-" . $courseName); $first = 0; } else { $treeArray['lesson_' . $current['id'] . '_' . $courseId . "_" . $courseId] = str_replace("'", "'", $offset . "-" . $courseName); } } } } $iterator->next(); $current = $iterator->current(); // $current if ($current['parent_direction_ID'] != $current_level_father) { $offset .= "-"; $current_level_father = $current['parent_direction_ID']; } } if ($returnClassedHTML) { $htmlString = '<select id= "educational_criteria_row" name ="educational_criteria_row" onchange="createQuestionsSelect(this)" mySelectedIndex = "0">'; if ($showQuestions) { $result = eF_getTableData("questions", "lessons_ID, count(lessons_ID) as quests", "type <> 'raw_text'", "", "lessons_ID"); $lessonQuestions = array(); foreach ($result as $lesson) { if ($lesson['quests'] > 0) { $lessonQuestions[$lesson['lessons_ID']] = $lesson['quests']; } } } foreach ($treeArray as $key => $value) { $extras = " "; $htmlString .= '<option'; if (strpos($key, "direction_") === 0) { $directions_ID = strrchr($key, "_"); $htmlString .= ' value = "direction' . $directions_ID . '" style="background-color:maroon; color:white"'; $course_ID = substr($directions_ID, 1); if ($showQuestions) { $questions = $directionsQuestions[$directions_ID]; if ($questions) { $extras = ' (' . $questions . ')'; } else { $extras = ' (0)'; } } } else { if (strpos($key, "course_") === 0) { $course_ID = strrchr($key, "_"); $htmlString .= ' value = "course' . $course_ID . '" style="background-color:green; color:white"'; $course_ID = substr($course_ID, 1); if ($showQuestions) { $questions = $courses[$course_ID]->course['questions']; if ($questions) { $extras = ' (' . $questions . ')'; } else { $extras = ' (0)'; } } } else { $htmlString .= ' value = "lesson' . strrchr($key, "_") . '" '; if ($showQuestions) { $lessonId = substr(strrchr($key, "_"), 1); if ($showQuestions) { if ($lessonQuestions[$lessonId]) { $extras .= "(" . $lessonQuestions[$lessonId] . ")"; } else { $extras .= "(0)"; } } } } } $htmlString .= ">" . $value . $extras . "</option>"; } $htmlString .= "</select>"; // If no lessons or anything is found, then an empty select or array should be returned return $htmlString; } return $treeArray; }
/** * Create HTML representation of the content tree * * This function is used to create an HTML representation of the content tree * The representation is based on javascript library drag_drop_folder_tree.js * If an iterator is not specified, then the tree displayed corrresponds to the default * tree (excluding inactive units). Otherwise, the specified iterator is used. * $treeId should be specified in case there will be more than one trees on the same page. * $options specifies the appearance and behaviour of the tree. Possible options are: * <br/>- edit (true/false) * <br/>- delete (true/false) * <br/>- activate (true/false) * <br/>- drag (true/false) * <br/>- noclick (true/false) * <br/>- selectedNode (node id) * <br/>- truncateNames (maximum length) * <br/>- expand (true/false) * <br/>- tree_root (true/false) (whether to display a root node) * <br/>- show_hide (true/false) (whether to display the show/hide header) * <br/>- onclick (function) * <br/>- custom (any custom code to be put next to each unit name) * <br/>Example: * <code> * echo $content -> toHTML(); //Display tree with all defaults * $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($content -> tree), RecursiveIteratorIterator :: SELF_FIRST), array('ctg_type' => 'theory')); * echo $content -> toHTML($iterator); //Display tree with only theory nodes * echo $content -> toHTML($iterator, 'theory_tree') //Display tree with only theory nodes, having id 'theory_tree' * echo $content -> toHTML($iterator, 'theory_tree', array('edit' => 1, 'delete' => 1)) //Display tree with only theory nodes, having id 'theory_tree', and which is editable and deleteable * </code> * * @param RecursiveIteratorIterator $iterator The content tree iterator * @param string $treeId The HTML id that will be assigned to the tree. It MUST start with 'dhtml_' * @param array $options Behaviour options for the tree * @param array $scormState An array that lists special parameters for depicting SCO state (applicable to scorm 2004 content only) * @return string The HTML code * @since 3.5.0 * @access public */ public function toHTML($iterator = false, $treeId = false, $options = array(), $scormState = array()) { !isset($options['hideFeedback']) ? $options['hideFeedback'] = false : null; !isset($options['onclick']) ? $options['onclick'] = false : null; !isset($options['custom']) ? $options['custom'] = false : null; !isset($options['tree_root']) ? $options['tree_root'] = false : null; isset($options['selectedNode']) or $options['selectedNode'] = ''; //Decide whether the tree is draggable isset($options['drag']) && $options['drag'] ? $nodrag = 'false' : ($nodrag = 'true'); //Should the tree expand isset($options['expand']) && $options['expand'] ? $expand = $options['expand'] : null; //Show the expand all/collapse all link !isset($options['show_hide']) || $options['show_hide'] ? $showHide = true : ($showHide = false); if (!$iterator) { $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->tree), RecursiveIteratorIterator::SELF_FIRST), array('active' => 1)); //Default iterator excludes non-active units } if (!$treeId) { $treeId = 'dhtml_tree'; } $iterator->rewind(); $current = $iterator->current(); $depth = $iterator->getDepth(); $initDepth = $depth; $treeString = ''; $count = 0; //Counts the total number of nodes, used to signify whether the tree has content if (!$current && !$options['tree_root']) { //Completely empty tree $treeString .= '<span class = "emptyCategory">' . _NOCONTENTFOUND . "</span>"; } while ($iterator->valid()) { $iterator->next(); //$current['ctg_type'] == 'tests' ? $contentType = 'tests' : $contentType = 'content'; if ($current['ctg_type'] == "tests" || $current['ctg_type'] == "feedback") { $contentType = $current['ctg_type']; } else { $contentType = 'content'; } $scorm2004 = in_array($current['scorm_version'], EfrontContentTree::$scorm2004Versions); $linkClass = array(); $liClass = array(); $tooltip = array(); $fullName = htmlspecialchars($current['name']); //Full name will be needed for the link title, which is never truncated //Decide whether the unit name will be truncated $unitName = htmlspecialchars($current['name']); if (isset($options['truncateNames']) && mb_strlen($current['name']) > $options['truncateNames']) { $unitName = mb_substr(htmlspecialchars($current['name']), 0, $options['truncateNames']) . '...'; } //Create the activate/deactivate link //isset($options['activate']) && $options['activate'] ? $activateLink = '<a href = "'.basename($_SERVER['PHP_SELF']).'?ctg='.$contentType.'&op=unit_order&activate_unit='.$current['id'].'"><img style = "vertical-align:middle" src = "images/16x16/trafficlight_green.png" title = "'._ACTIVATE.'" alt = "'._ACTIVATE.'" border = "0" /></a>' : $activateLink = ''; $activateLink = ''; if (isset($options['activate']) && $options['activate']) { if ($current['active']) { $image = 'trafficlight_green.png'; $title = _DEACTIVATE; } else { $image = 'trafficlight_red.png'; $title = _ACTIVATE; } $activateLink = '<a href = "javascript:void(0)" onclick = "JSTreeObj.activateUnit(null, $(\'node' . $current['id'] . '\').down().next().next())"><img class = "handle" src = "images/16x16/' . $image . '" title = "' . $title . '" alt = "' . $title . '" /></a>'; } //Create the edit link $editLink = ''; if (isset($options['edit']) && $options['edit'] && !$current['linked_to']) { $editLink = '<a href = "' . basename($_SERVER['PHP_SELF']) . '?ctg=' . $contentType . '&edit=' . $current['id'] . '"><img class = "handle" src = "images/16x16/edit.png" title = "' . _EDIT . '" alt = "' . _EDIT . '" /></a>'; } //Create the delete link $deleteLink = ''; if (isset($options['delete']) && $options['delete']) { $deleteLink = '<a href = "javascript:void(0)" onclick = "JSTreeObj.deleteUnit(null, $(\'node' . $current['id'] . '\').down().next().next())"><img src = "images/16x16/error_delete.png" title = "' . _DELETE . '" alt = "' . _DELETE . '" class = "handle"/></a>'; } //Create the target link $targetLink = basename($_SERVER['PHP_SELF']) . '?view_unit=' . $current['id']; //Set the selected node if (isset($options['selectedNode']) && $options['selectedNode'] == $current['id']) { $linkClass[] = 'drag_tree_current'; } //Set the display style according to whether the unit has data if ($current['data'] == '' && $current['ctg_type'] == 'scorm' && $scorm2004) { $linkClass[] = 'treeHeader'; } else { if ($current['data'] == '' && $current['ctg_type'] != 'tests' && $current['ctg_type'] != 'feedback') { $linkClass[] = 'treeNoContent'; $fullName .= ' (' . _EMPTYUNIT . ')'; $targetLink = 'javascript:void(0)'; } } //Set the display style according to whether the unit is inactive if ($current['active'] == 0) { $linkClass[] = 'inactiveLink'; } if ($current['ctg_type'] == "feedback" && $options['hideFeedback'] == true) { $linkClass[] = 'inactiveLink'; $targetLink = 'javascript:void(0)'; } $showTools = true; if (G_VERSIONTYPE != 'community') { #cpp#ifndef COMMUNITY if (G_VERSIONTYPE != 'standard') { #cpp#ifndef STANDARD //SCORM 2004: Handles SCORM requirements on navigation $startLink = ''; $display = ''; if ($scorm2004) { if ($_SESSION['package_ID'] == $current['package_ID']) { $targetLink = basename($_SERVER['PHP_SELF']) . '?ctg=content&navigation=choice&target=' . $current['id'] . '&package_ID=' . $current['package_ID']; $options['onclick'] = false; if ($scormState['choice'][$current['id']] == 'disabled') { $linkClass[] = 'inactiveLink'; $options['onclick'] = "return false"; } else { if ($scormState['choice'][$current['id']] == 'hidden') { $display = 'display:none'; } } if ($current['package_ID'] != $current['id']) { $showTools = false; //We don't want edit/delete tools for package children } } else { if ($current['package_ID'] == $current['id']) { $targetLink = basename($_SERVER['PHP_SELF']) . '?ctg=content&package_ID=' . $current['package_ID']; } else { $display = 'display:none'; } } } } #cpp#endif } #cpp#endif if (isset($options['noclick']) && $options['noclick']) { $targetLink = 'javascript:void(0)'; } if (!$options['onclick'] && $targetLink == 'javascript:void(0)') { $linkClass[] = 'treeUnclickable'; } //Set the class name, based on the unit status if ($current['ctg_type'] == 'scorm') { $ctgType = 'theory'; } else { if ($current['ctg_type'] == 'scorm_test') { $ctgType = 'tests'; } else { $ctgType = $current['ctg_type']; } } if (isset($current['incomplete']) && $current['incomplete']) { $liClass[] = $ctgType . '_incomplete'; $tooltip[] = '(' . _TESTSTARTEDAT . ': ' . formatTimestamp($current['incomplete'], 'time') . ')'; } else { if (isset($current['failed']) && $current['failed']) { $liClass[] = $ctgType . '_failed'; } else { if (isset($current['seen']) && $current['seen'] || isset($current['completed']) && $current['completed']) { $liClass[] = $ctgType . '_passed'; } else { $liClass[] = $ctgType; } } } /* if ((isset($current['seen']) && $current['seen']) ||(isset($current['completed']) && $current['completed']) || (isset($current['passed']) && $current['passed'])) { $liClass[] = $ctgType.'_passed'; } else if (isset($current['incomplete']) && $current['incomplete'] && !$current['failed']) { $liClass[] = $ctgType.'_incomplete'; $tooltip[] = '('._TESTSTARTEDAT.': '.formatTimestamp($current['incomplete'], 'time').')'; } else if (isset($current['failed']) && $current['failed']) { $liClass[] = $ctgType.'_failed'; } else { $liClass[] = $ctgType; } */ if ($options['onclick']) { $onclick = $options['onclick']; } else { if (in_array('treeUnclickable', $linkClass)) { $onclick = 'return false'; } else { $onclick = ''; } } //$toolsString = '<span class = "toolsDiv" style = "position:absolute">'.$activateLink.$editLink.$deleteLink.'</span>'; $toolsString = '<span>' . $activateLink . $startLink . $editLink . $deleteLink . $options['custom'][$current['id']] . '</span>'; if (!$showTools) { unset($toolsString); } $treeString .= ' <li style = "white-space:nowrap;' . $display . '" class = "' . implode(" ", $liClass) . '" id = "node' . $current['id'] . '" noDrag = "' . $nodrag . '" noRename = "true" noDelete = "true"> <a onclick = "' . $onclick . '" class = "' . (!$current['active'] ? 'treeinactive' : '') . ' treeLink ' . implode(" ", $linkClass) . '" href = "' . $targetLink . '" title = "' . $fullName . ' ' . implode(" ", $tooltip) . '">' . $unitName . " </a> " . $toolsString; $iterator->getDepth() > $depth ? $treeString .= '<ul>' : ($treeString .= '</li>'); for ($i = $depth; $i > $iterator->getDepth() && $i > $initDepth; $i--) { $treeString .= '</ul></li>'; } $current = $iterator->current(); $depth = $iterator->getDepth(); $count++; } $str = ''; if ($showHide) { $str .= ' <div id = "expand_collapse_div' . $treeId . '" ' . (isset($expand) ? 'expand = "' . $expand . '"' : null) . '> <b><a href = "javascript:void(0)" onclick = "treeObj.setTreeId(\'' . $treeId . '\');treeObj.collapseAll();">' . _HIDEALL . '</a></b> / <b><a href = "javascript:void(0)" onclick = "treeObj.setTreeId(\'' . $treeId . '\');treeObj.expandAll();" >' . _SHOWALL . '</a></b><br/> </div>'; } $str .= ' <table> <tr><td> <ul id = "' . $treeId . '" class = "dhtmlgoodies_tree" selectedNode = "' . $options['selectedNode'] . '">'; if ($options['tree_root']) { $str .= ' <li class = "theory" id = "0" noDrag = "false"> <a class = "treeactive treeLink" style = "white-space:nowrap;" href = "javascript:void(0)" title = "' . _TREEROOT . '">' . _TREEROOT . "</a> "; } $str .= $treeString . ' </ul> </td></tr> </table>'; return $str; }
public function getPreviousSiblingNode($queryNode) { $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->tree), RecursiveIteratorIterator::SELF_FIRST)); //Create iterators for the tree $iterator->rewind(); //Initialize iterator while ($iterator->valid() && $iterator->key() != $queryNode['parent_content_ID']) { //Advance iterator until we reach the designated node $iterator->next(); } if ($iterator->valid()) { $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator->current()))); $iterator->rewind(); //Initialize iterator while ($iterator->valid() && $iterator->key() != $queryNode['id']) { //Advance iterator until we reach the designated node $previousNode = $iterator->current(); $iterator->next(); } if ($iterator->valid()) { if (!isset($previousNode)) { //The designated node was apparently the first one, so return false return false; } else { if (!$queryNode) { $this->currentNodeId = $previousNode['id']; //If a $queryNode was not specified, we must set the internal pointer to the previous node we just found } //$previousNode = $this -> filterOutChildren(new RecursiveArrayIterator($previousNode)); //Cut off node's children return $previousNode; } } } else { throw new EfrontTreeException(_NODEDOESNOTEXIST . ': ' . $queryNode['id'], EfrontTreeException::NODE_NOT_EXISTS); } }