From ce5a4f3284824e7f1ec9cfda733dfafda2171dd3 Mon Sep 17 00:00:00 2001 From: thepurpleblob Date: Wed, 22 Feb 2006 10:47:20 +0000 Subject: [PATCH] Split out category class, and moved display part of code to it's own section. About to make some changes related to large numbers of categories (pagination etc). --- mod/quiz/category.php | 746 ++---------------------------------- mod/quiz/category_class.php | 706 ++++++++++++++++++++++++++++++++++ 2 files changed, 728 insertions(+), 724 deletions(-) create mode 100644 mod/quiz/category_class.php diff --git a/mod/quiz/category.php b/mod/quiz/category.php index 68084988c8b..5b5ccf90b4c 100644 --- a/mod/quiz/category.php +++ b/mod/quiz/category.php @@ -11,6 +11,7 @@ require_once("../../config.php"); require_once("locallib.php"); + require_once("category_class.php"); // get values from form $param = new stdClass(); @@ -42,27 +43,6 @@ error("Only teachers authorized to edit the course '{$course->fullname}' can use this page!"); } - /// Header: - - if (isset($SESSION->modform->instance) and $quiz = get_record('quiz', 'id', $SESSION->modform->instance)) { - $strupdatemodule = isteacheredit($course->id) - ? update_module_button($SESSION->modform->cmid, $course->id, get_string('modulename', 'quiz')) - : ""; - print_header_simple(get_string('editcategories', 'quiz'), '', - "id\">".get_string('modulenameplural', 'quiz').''. - " -> id\">".format_string($quiz->name).''. - ' -> '.get_string('editcategories', 'quiz'), - "", "", true, $strupdatemodule); - $currenttab = 'edit'; - $mode = 'categories'; - include('tabs.php'); - } else { - print_header_simple(get_string('editcategories', 'quiz'), '', - "id\">".get_string('modulenameplural', 'quiz').''. - '-> '.get_string('editquestions', 'quiz').''. - ' -> '.get_string('editcategories', 'quiz')); - } - $qcobject = new quiz_category_object(); $qcobject->set_course($course); @@ -111,6 +91,27 @@ // DISPLAY //========== + /// Header: + + if (isset($SESSION->modform->instance) and $quiz = get_record('quiz', 'id', $SESSION->modform->instance)) { + $strupdatemodule = isteacheredit($course->id) + ? update_module_button($SESSION->modform->cmid, $course->id, get_string('modulename', 'quiz')) + : ""; + print_header_simple(get_string('editcategories', 'quiz'), '', + "id\">".get_string('modulenameplural', 'quiz').''. + " -> id\">".format_string($quiz->name).''. + ' -> '.get_string('editcategories', 'quiz'), + "", "", true, $strupdatemodule); + $currenttab = 'edit'; + $mode = 'categories'; + include('tabs.php'); + } else { + print_header_simple(get_string('editcategories', 'quiz'), '', + "id\">".get_string('modulenameplural', 'quiz').''. + '-> '.get_string('editquestions', 'quiz').''. + ' -> '.get_string('editcategories', 'quiz')); + } + // display the user interface $qcobject->display_user_interface(); @@ -118,708 +119,5 @@ -/** -* Class quiz_category_object -* -* Used for handling changes to the quiz categories -* -*/ -class quiz_category_object { - - var $str; - var $pixpath; - var $edittable; - var $newtable; - var $tab; - var $tabsize = 3; - var $categories; - var $categorystrings; - var $defaultcategory; - var $course; - -/** -* Constructor -* -* Gets necessary strings and sets relevant path information -* -*/ - function quiz_category_object() { - global $CFG; - - $this->tab = str_repeat(' ', $this->tabsize); - - $this->str->course = get_string('course'); - $this->str->category = get_string('category', 'quiz'); - $this->str->categoryinfo = get_string('categoryinfo', 'quiz'); - $this->str->questions = get_string('questions', 'quiz'); - $this->str->add = get_string('add'); - $this->str->delete = get_string('delete'); - $this->str->moveup = get_string('moveup'); - $this->str->movedown = get_string('movedown'); - $this->str->edit = get_string('editthiscategory'); - $this->str->hide = get_string('hide'); - $this->str->publish = get_string('publish', 'quiz'); - $this->str->order = get_string('order'); - $this->str->parent = get_string('parent', 'quiz'); - $this->str->add = get_string('add'); - $this->str->action = get_string('action'); - $this->str->top = get_string('top', 'quiz'); - $this->str->addcategory = get_string('addcategory', 'quiz'); - $this->str->editcategory = get_string('editcategory', 'quiz'); - $this->str->cancel = get_string('cancel'); - $this->str->editcategories = get_string('editcategories', 'quiz'); - $this->pixpath = $CFG->pixpath; - - } - -/** -* Sets the course for this object -* -* @param object course -*/ - function set_course($course) { - $this->course = $course; - } - -/** -* Displays the user interface -* -* @param object modform -*/ - function display_user_interface() { - $this->initialize(); - - /// Interface for adding a new category: - print_heading_with_help($this->str->addcategory, 'categories_edit', 'quiz'); - $this->output_new_table(); - echo '
'; - - /// Interface for editing existing categories - print_heading_with_help($this->str->editcategories, 'categories', 'quiz'); - $this->output_edit_table(); - echo '
'; - - } - - -/** -* Initializes this classes general category-related variables -* -*/ - function initialize() { - - /// Get the existing categories - if (!$this->defaultcategory = quiz_get_default_category($this->course->id)) { - error("Error: Could not find or make a category!"); - } - - $this->categories = $this->get_quiz_categories(null, "parent, sortorder, name ASC"); - - $this->categories = $this->arrange_categories($this->categories); - - // create the array of id=>full_name strings - $this->categorystrings = $this->expanded_category_strings($this->categories); - } - - - -/** -* Outputs a table to allow entry of a new category -* -*/ - function output_new_table() { - global $USER; - $publishoptions[0] = get_string("no"); - $publishoptions[1] = get_string("yes"); - - $this->newtable->head = array ($this->str->parent, $this->str->category, $this->str->categoryinfo, $this->str->publish, $this->str->action); - $this->newtable->width = 200; - $this->newtable->data[] = array(); - $this->newtable->tablealign = 'center'; - - /// Each section below adds a data cell to the table row - - - $viableparents[0] = $this->str->top; - $viableparents = $viableparents + $this->categorystrings; - $this->newtable->align['parent'] = "left"; - $this->newtable->wrap['parent'] = "nowrap"; - $row['parent'] = choose_from_menu ($viableparents, "newparent", $this->str->top, "", "", "", true); - - $this->newtable->align['category'] = "left"; - $this->newtable->wrap['category'] = "nowrap"; - $row['category'] = ''; - - $this->newtable->align['info'] = "left"; - $this->newtable->wrap['info'] = "nowrap"; - $row['info'] = ''; - - $this->newtable->align['publish'] = "left"; - $this->newtable->wrap['publish'] = "nowrap"; - $row['publish'] = choose_from_menu ($publishoptions, "newpublish", "", "", "", "", true); - - $this->newtable->align['action'] = "left"; - $this->newtable->wrap['action'] = "nowrap"; - $row['action'] = ''; - - - $this->newtable->data[] = $row; - - // wrap the table in a form and output it - echo '
'; - echo "sesskey\" />"; - echo ''; - echo ''; - print_table($this->newtable); - echo '
'; - } - -/** -* Outputs a table to allow editing/rearranging of existing categories -* -* $this->initialize() must have already been called -* -* @param object course -*/ - function output_edit_table() { - $this->edittable->head = array ($this->str->category, $this->str->categoryinfo, $this->str->questions, $this->str->publish, - $this->str->delete, $this->str->order, $this->str->parent); - $this->edittable->width = 200; - $this->edittable->tablealign = 'center'; - - $courses = $this->course->shortname; - - $this->build_edit_table_body($this->categories); - print_table($this->edittable); - } -/** -* Recursively builds up the edit-categories table body -* -* @param array categories contains category objects in a tree representation -* @param mixed courses String with shortname of course | array containing courseid=>shortname -* @param int depth controls the indenting -*/ - function build_edit_table_body($categories, $depth = 0) { - $countcats = count($categories); - $count = 0; - $first = true; - $last = false; - - foreach ($categories as $category) { - $count++; - if ($count == $countcats) { - $last = true; - } - $up = $first ? false : true; - $down = $last ? false : true; - $first = false; - $this->quiz_edit_category_row($category, $depth, $up, $down); - if (isset($category->children)) { - $this->build_edit_table_body($category->children, $depth + 1); - } - } - } - -/** -* gets all the courseids for the given categories -* -* @param array categories contains category objects in a tree representation -* @return array courseids flat array in form categoryid=>courseid -*/ - function get_course_ids($categories) { - $courseids = array(); - foreach ($categories as $key=>$cat) { - $courseids[$key] = $cat->course; - if (!empty($cat->children)) { - $courseids = array_merge($courseids, $this->get_course_ids($cat->children)); - } - } - return $courseids; - } - -/** -* Constructs each row of the edit-categories table -* -* @param object category -* @param int depth controls the indenting -* @param string shortname short name of the course -* @param boolean up can it be moved up? -* @param boolean down can it be moved down? -*/ - function quiz_edit_category_row($category, $depth, $up = false, $down = false) { - global $USER; - $fill = str_repeat($this->tab, $depth); - - $linkcss = $category->publish ? ' class="published"' : ' class="unpublished"'; - - /// Each section below adds a data cell to this table row - - $this->edittable->align["$category->id.name"] = "left"; - $this->edittable->wrap["$category->id.name"] = "nowrap"; - $row["$category->id.name"] = '' .$this->str->edit. ' ' . $fill . $category->name . ''; - - $this->edittable->align["$category->id.info"] = "left"; - $this->edittable->wrap["$category->id.info"] = "nowrap"; - $row["$category->id.info"] = '' . $category->info . ''; - - $this->edittable->align["$category->id.qcount"] = "center"; - $row["$category->id.qcount"] = $category->questioncount; - - $this->edittable->align["$category->id.publish"] = "center"; - $this->edittable->wrap["$category->id.publish"] = "nowrap"; - if (!empty($category->publish)) { - $row["$category->id.publish"] = '' .$this->str->hide. ' '; - } else { - $row["$category->id.publish"] = '' .$this->str->publish. ' '; - } - - if ($category->id != $this->defaultcategory->id) { - $this->edittable->align["$category->id.delete"] = "center"; - $this->edittable->wrap["$category->id.delete"] = "nowrap"; - $row["$category->id.delete"] = '' .$this->str->delete. ' '; - } else { - $row["$category->id.delete"] = ''; - } - - $this->edittable->align["$category->id.order"] = "left"; - $this->edittable->wrap["$category->id.order"] = "nowrap"; - $icons = ''; - if ($up) { - $icons .= ' - ' . $this->str->moveup. ' '; - } - if ($down) { - $icons .= ' - ' .$this->str->movedown. ' '; - } - $row["$category->id.order"]= $icons; - - $this->edittable->align["$category->id.moveto"] = "left"; - $this->edittable->wrap["$category->id.moveto"] = "nowrap"; - if ($category->id != $this->defaultcategory->id) { - $viableparents = $this->categorystrings; - $this->set_viable_parents($viableparents, $category); - $viableparents = array(0=>$this->str->top) + $viableparents; - - $row["$category->id.moveto"] = popup_form ("category.php?id={$this->course->id}&move={$category->id}&sesskey=$USER->sesskey&moveto=", - $viableparents, "moveform{$category->id}", "$category->parent", "", "", "", true); - } else { - $row["$category->id.moveto"]='---'; - } - - - $this->edittable->data[$category->id] = $row; - } - - - function edit_single_category($categoryid) { - /// Interface for adding a new category - global $USER; - $this->initialize(); - - /// Interface for editing existing categories - if ($category = get_record("quiz_categories", "id", $categoryid)) { - echo '

'; - echo $this->str->edit; - helpbutton("categories_edit", $this->str->editcategory, "quiz"); - echo '

'; - echo '
'; - $this->output_edit_single_table($category); - echo '
'; - echo '

- - -

'; - print_footer($this->course); - exit; - } else { - error("Category $categoryid not found", "category.php?id={$this->course->id}"); - } - } - - -/** -* Outputs a table to allow editing of an existing category -* -* @param object category -*/ - function output_edit_single_table($category) { - global $USER; - $publishoptions[0] = get_string("no"); - $publishoptions[1] = get_string("yes"); - $strupdate = get_string('update'); - - unset ($edittable); - - $edittable->head = array ($this->str->parent, $this->str->category, $this->str->categoryinfo, $this->str->publish, $this->str->action); - $edittable->width = 200; - $edittable->data[] = array(); - $edittable->tablealign = 'center'; - - /// Each section below adds a data cell to the table row - - $viableparents = $this->categorystrings; - $this->set_viable_parents($viableparents, $category); - $viableparents = array(0=>$this->str->top) + $viableparents; - $edittable->align['parent'] = "left"; - $edittable->wrap['parent'] = "nowrap"; - $row['parent'] = choose_from_menu ($viableparents, "updateparent", "{$category->parent}", "", "", "", true); - - $edittable->align['category'] = "left"; - $edittable->wrap['category'] = "nowrap"; - $row['category'] = ''; - - $edittable->align['info'] = "left"; - $edittable->wrap['info'] = "nowrap"; - $row['info'] = ''; - - $edittable->align['publish'] = "left"; - $edittable->wrap['publish'] = "nowrap"; - $selected = (boolean)$category->publish ? 1 : 0; - $row['publish'] = choose_from_menu ($publishoptions, "updatepublish", $selected, "", "", "", true); - - $edittable->align['action'] = "left"; - $edittable->wrap['action'] = "nowrap"; - $row['action'] = ''; - - $edittable->data[] = $row; - - // wrap the table in a form and output it - echo '

'; - echo "sesskey\" />"; - echo ''; - echo ''; - print_table($edittable); - echo '

'; - } - - -/** -* Creates an array of "full-path" category strings -* Structure: -* key => string -* where key is the category id, and string contains the name of all ancestors as well as the particular category name -* E.g. '123'=>'Language / English / Grammar / Modal Verbs" -* -* @param array $categories an array containing categories arranged in a tree structure -*/ - function expanded_category_strings($categories, $parent=null) { - $prefix = is_null($parent) ? '' : "$parent / "; - $categorystrings = array(); - foreach ($categories as $key => $category) { - $expandedname = "$prefix$category->name"; - $categorystrings[$key] = $expandedname; - if (isset($category->children)) { - $categorystrings = $categorystrings + $this->expanded_category_strings($category->children, $expandedname); - } - } - return $categorystrings; - } - - -/** -* Arranges the categories into a hierarchical tree -* -* If a category has children, it's "children" property holds an array of children -* The questioncount for each category is also calculated -* -* @param array records a flat list of the categories -* @return array categorytree a hierarchical list of the categories -*/ - function arrange_categories($records) { - //todo: get the question count for all records with one sql statement: select category, count(*) from quiz_questions group by category - $levels = array(); - - // build a levels array, which places each record according to it's depth from the top level - $parents = array(0); - while (!empty($parents)) { - $children = array(); - foreach ($records as $record) { - if (in_array($record->parent, $parents)) { - $children[] = $record->id; - } - } - if (!empty($children)) { - $levels[] = $children; - } - $parents = $children; - } - // if there is no hierarchy (e.g., if all records have parent == 0), set level[0] to these keys - if (empty($levels)) { - $levels[0] = array_keys($records); - } - - // build a hierarchical array that depicts the parent-child relationships of the categories - $categorytree = array(); - for ($index = count($levels) - 1; $index >= 0; $index--) { - foreach($levels[$index] as $key) { - $parentkey = $records[$key]->parent; - if (!($records[$key]->questioncount = count_records('quiz_questions', 'category', $records[$key]->id, 'hidden', 0, 'parent', '0'))) { - $records[$key]->questioncount = 0; - } - if ($parentkey == 0) { - $categorytree[$key] = $records[$key]; - } else { - $records[$parentkey]->children[$key] = $records[$key]; - } - } - } - return $categorytree; - } - - -/** -* Sets the viable parents -* -* Viable parents are any except for the category itself, or any of it's descendants -* The parentstrings parameter is passed by reference and changed by this function. -* -* @param array parentstrings a list of parentstrings -* @param object category -*/ - function set_viable_parents(&$parentstrings, $category) { - - unset($parentstrings[$category->id]); - if (isset($category->children)) { - foreach ($category->children as $child) { - $this->set_viable_parents($parentstrings, $child); - } - } - } - -/** -* Gets quiz categories -* -* @param int parent - if given, restrict records to those with this parent id. -* @param string sort - [[sortfield [,sortfield]] {ASC|DESC}] -* @return array categories -*/ - function get_quiz_categories($parent=null, $sort="sortorder ASC") { - - if (is_null($parent)) { - $categories = get_records('quiz_categories', 'course', "{$this->course->id}", $sort); - } else { - $select = "parent = '$parent' AND course = '{$this->course->id}'"; - $categories = get_records_select('quiz_categories', $select, $sort); - } - return $categories; - } - - -/** -* Deletes an existing quiz category -* -* @param int deletecat id of category to delete -* @param int destcategoryid id of category which will inherit the orphans of deletecat -*/ - function delete_category($deletecat, $destcategoryid = null) { - global $USER; - - if (!$category = get_record("quiz_categories", "id", $deletecat)) { // security - error("No such category $deletecat!", "category.php?id={$this->course->id}"); - } - - if (!is_null($destcategoryid)) { // Need to move some questions before deleting the category - if (!$category2 = get_record("quiz_categories", "id", $destcategoryid)) { // security - error("No such category $destcategoryid!", "category.php?id={$this->course->id}"); - } - if (! set_field('quiz_questions', 'category', $category2, 'category', $category1)) { - error("Error while moving questions from category '$category->name' to '$category2->name'", "category.php?id={$this->course->id}"); - } - - } else { - // todo: delete any hidden questions that are not actually in use any more - if ($count = count_records("quiz_questions", "category", $category->id)) { - $vars->name = $category->name; - $vars->count = $count; - print_simple_box(get_string("categorymove", "quiz", $vars), "center"); - $this->initialize(); - $categorystrings = $this->categorystrings; - unset ($categorystrings[$category->id]); - echo "

"; - echo "sesskey\" />"; - echo "course->id}\" />"; - echo "id\" />"; - choose_from_menu($categorystrings, "confirm", "", ""); - echo ""; - echo "str->cancel}\" />"; - echo "

"; - print_footer($this->course); - exit; - } - } - delete_records("quiz_categories", "id", $category->id); - - /// Send the children categories to live with their grandparent - if ($childcats = get_records("quiz_categories", "parent", $category->id)) { - foreach ($childcats as $childcat) { - if (! set_field("quiz_categories", "parent", $category->parent, "id", $childcat->id)) { - error("Could not update a child category!", "category.php?id={$this->course->id}"); - } - } - } - - /// Finally delete the category itself - if (delete_records("quiz_categories", "id", $category->id)) { - notify(get_string("categorydeleted", "quiz", $category->name), 'green'); - } - } - -/** -* Moves a category up or down in the display order -* -* @param string direction up|down -* @param int categoryid id of category to move -*/ - function move_category_up_down ($direction, $categoryid) { - /// Move a category up or down - $swapcategory = NULL; - $movecategory = NULL; - - if ($direction == 'up') { - if ($movecategory = get_record("quiz_categories", "id", $categoryid)) { - $categories = $this->get_quiz_categories("$movecategory->parent", 'parent, sortorder, name'); - - foreach ($categories as $category) { - if ($category->id == $movecategory->id) { - break; - } - $swapcategory = $category; - } - } - } - if ($direction == 'down') { - if ($movecategory = get_record("quiz_categories", "id", $categoryid)) { - $categories = $this->get_quiz_categories("$movecategory->parent", 'parent, sortorder, name'); - $choosenext = false; - foreach ($categories as $category) { - if ($choosenext) { - $swapcategory = $category; - break; - } - if ($category->id == $movecategory->id) { - $choosenext = true; - } - } - } - } - if ($swapcategory and $movecategory) { // Renumber everything for robustness - $count=0; - foreach ($categories as $category) { - $count++; - if ($category->id == $swapcategory->id) { - $category = $movecategory; - } else if ($category->id == $movecategory->id) { - $category = $swapcategory; - } - if (! set_field("quiz_categories", "sortorder", $count, "id", $category->id)) { - notify("Could not update that category!"); - } - } - } - } - -/** -* Changes the parent of a category -* -* @param int categoryid -* @param int parentid -*/ - function move_category($categoryid, $parentid) { - /// Move a category to a new parent - - if ($tempcat = get_record("quiz_categories", "id", $categoryid)) { - if ($tempcat->parent != $parentid) { - if (! set_field("quiz_categories", "parent", $parentid, "id", $tempcat->id)) { - notify("Could not update that category!"); - } - } - } - } - -/** -* Changes the published status of a category -* -* @param boolean publish -* @param int categoryid -*/ - function publish_category($publish, $categoryid) { - /// Hide or publish a category - - $publish = ($publish == false) ? 0 : 1; - $tempcat = get_record("quiz_categories", "id", $categoryid); - if ($tempcat) { - if (! set_field("quiz_categories", "publish", $publish, "id", $tempcat->id)) { - notify("Could not update that category!"); - } - } - } - -/** -* Creates a new category with given params -* -* @param int $newparent id of the parent category -* @param string $newcategory the name for the new category -* @param string $newinfo the info field for the new category -* @param int $newpublish whether to publish the category -* @param int $newcourse the id of the associated course -*/ - function add_category($newparent, $newcategory, $newinfo, $newpublish, $newcourse) { - - if ($newparent) { - // first check that the parent category is in the correct course - if(!(get_field('quiz_categories', 'course', 'id', $newparent) == $newcourse)) { - return false; - } - } - - $cat = NULL; - $cat->parent = $newparent; - $cat->name = $newcategory; - $cat->info = $newinfo; - $cat->publish = $newpublish; - $cat->course = $newcourse; - $cat->sortorder = 999; - $cat->stamp = make_unique_id_code(); - if (!insert_record("quiz_categories", $cat)) { - error("Could not insert the new quiz category '$newcategory'", "category.php?id={$newcourse}"); - } else { - notify(get_string("categoryadded", "quiz", $newcategory), 'green'); - } - - } - -/** -* Updates an existing category with given params -* -* @param int updateid -* @param int updateparent -* @param string updatename -* @param string updateinfo -* @param int updatepublish -* @param int courseid the id of the associated course -*/ - function update_category($updateid, $updateparent, $updatename, $updateinfo, $updatepublish, $courseid) { - - $cat = NULL; - $cat->id = $updateid; - $cat->parent = $updateparent; - $cat->name = $updatename; - $cat->info = $updateinfo; - $cat->publish = $updatepublish; - if (!update_record("quiz_categories", $cat)) { - error("Could not update the category '$updatename'", "category.php?id={$courseid}"); - } else { - notify(get_string("categoryupdated", 'quiz'), 'green'); - } - } - -} ?> diff --git a/mod/quiz/category_class.php b/mod/quiz/category_class.php new file mode 100644 index 00000000000..62c4a23a90f --- /dev/null +++ b/mod/quiz/category_class.php @@ -0,0 +1,706 @@ +tab = str_repeat(' ', $this->tabsize); + + $this->str->course = get_string('course'); + $this->str->category = get_string('category', 'quiz'); + $this->str->categoryinfo = get_string('categoryinfo', 'quiz'); + $this->str->questions = get_string('questions', 'quiz'); + $this->str->add = get_string('add'); + $this->str->delete = get_string('delete'); + $this->str->moveup = get_string('moveup'); + $this->str->movedown = get_string('movedown'); + $this->str->edit = get_string('editthiscategory'); + $this->str->hide = get_string('hide'); + $this->str->publish = get_string('publish', 'quiz'); + $this->str->order = get_string('order'); + $this->str->parent = get_string('parent', 'quiz'); + $this->str->add = get_string('add'); + $this->str->action = get_string('action'); + $this->str->top = get_string('top', 'quiz'); + $this->str->addcategory = get_string('addcategory', 'quiz'); + $this->str->editcategory = get_string('editcategory', 'quiz'); + $this->str->cancel = get_string('cancel'); + $this->str->editcategories = get_string('editcategories', 'quiz'); + $this->pixpath = $CFG->pixpath; + + } + +/** +* Sets the course for this object +* +* @param object course +*/ + function set_course($course) { + $this->course = $course; + } + +/** +* Displays the user interface +* +* @param object modform +*/ + function display_user_interface() { + $this->initialize(); + + /// Interface for adding a new category: + print_heading_with_help($this->str->addcategory, 'categories_edit', 'quiz'); + $this->output_new_table(); + echo '
'; + + /// Interface for editing existing categories + print_heading_with_help($this->str->editcategories, 'categories', 'quiz'); + $this->output_edit_table(); + echo '
'; + + } + + +/** +* Initializes this classes general category-related variables +* +*/ + function initialize() { + + /// Get the existing categories + if (!$this->defaultcategory = quiz_get_default_category($this->course->id)) { + error("Error: Could not find or make a category!"); + } + + $this->categories = $this->get_quiz_categories(null, "parent, sortorder, name ASC"); + + $this->categories = $this->arrange_categories($this->categories); + + // create the array of id=>full_name strings + $this->categorystrings = $this->expanded_category_strings($this->categories); + } + + + +/** +* Outputs a table to allow entry of a new category +* +*/ + function output_new_table() { + global $USER; + $publishoptions[0] = get_string("no"); + $publishoptions[1] = get_string("yes"); + + $this->newtable->head = array ($this->str->parent, $this->str->category, $this->str->categoryinfo, $this->str->publish, $this->str->action); + $this->newtable->width = 200; + $this->newtable->data[] = array(); + $this->newtable->tablealign = 'center'; + + /// Each section below adds a data cell to the table row + + + $viableparents[0] = $this->str->top; + $viableparents = $viableparents + $this->categorystrings; + $this->newtable->align['parent'] = "left"; + $this->newtable->wrap['parent'] = "nowrap"; + $row['parent'] = choose_from_menu ($viableparents, "newparent", $this->str->top, "", "", "", true); + + $this->newtable->align['category'] = "left"; + $this->newtable->wrap['category'] = "nowrap"; + $row['category'] = ''; + + $this->newtable->align['info'] = "left"; + $this->newtable->wrap['info'] = "nowrap"; + $row['info'] = ''; + + $this->newtable->align['publish'] = "left"; + $this->newtable->wrap['publish'] = "nowrap"; + $row['publish'] = choose_from_menu ($publishoptions, "newpublish", "", "", "", "", true); + + $this->newtable->align['action'] = "left"; + $this->newtable->wrap['action'] = "nowrap"; + $row['action'] = ''; + + + $this->newtable->data[] = $row; + + // wrap the table in a form and output it + echo '
'; + echo "sesskey\" />"; + echo ''; + echo ''; + print_table($this->newtable); + echo '
'; + } + +/** +* Outputs a table to allow editing/rearranging of existing categories +* +* $this->initialize() must have already been called +* +* @param object course +*/ + function output_edit_table() { + $this->edittable->head = array ($this->str->category, $this->str->categoryinfo, $this->str->questions, $this->str->publish, + $this->str->delete, $this->str->order, $this->str->parent); + $this->edittable->width = 200; + $this->edittable->tablealign = 'center'; + + $courses = $this->course->shortname; + + $this->build_edit_table_body($this->categories); + print_table($this->edittable); + } +/** +* Recursively builds up the edit-categories table body +* +* @param array categories contains category objects in a tree representation +* @param mixed courses String with shortname of course | array containing courseid=>shortname +* @param int depth controls the indenting +*/ + function build_edit_table_body($categories, $depth = 0) { + $countcats = count($categories); + $count = 0; + $first = true; + $last = false; + + foreach ($categories as $category) { + $count++; + if ($count == $countcats) { + $last = true; + } + $up = $first ? false : true; + $down = $last ? false : true; + $first = false; + $this->quiz_edit_category_row($category, $depth, $up, $down); + if (isset($category->children)) { + $this->build_edit_table_body($category->children, $depth + 1); + } + } + } + +/** +* gets all the courseids for the given categories +* +* @param array categories contains category objects in a tree representation +* @return array courseids flat array in form categoryid=>courseid +*/ + function get_course_ids($categories) { + $courseids = array(); + foreach ($categories as $key=>$cat) { + $courseids[$key] = $cat->course; + if (!empty($cat->children)) { + $courseids = array_merge($courseids, $this->get_course_ids($cat->children)); + } + } + return $courseids; + } + +/** +* Constructs each row of the edit-categories table +* +* @param object category +* @param int depth controls the indenting +* @param string shortname short name of the course +* @param boolean up can it be moved up? +* @param boolean down can it be moved down? +*/ + function quiz_edit_category_row($category, $depth, $up = false, $down = false) { + global $USER; + $fill = str_repeat($this->tab, $depth); + + $linkcss = $category->publish ? ' class="published"' : ' class="unpublished"'; + + /// Each section below adds a data cell to this table row + + $this->edittable->align["$category->id.name"] = "left"; + $this->edittable->wrap["$category->id.name"] = "nowrap"; + $row["$category->id.name"] = '' .$this->str->edit. ' ' . $fill . $category->name . ''; + + $this->edittable->align["$category->id.info"] = "left"; + $this->edittable->wrap["$category->id.info"] = "nowrap"; + $row["$category->id.info"] = '' . $category->info . ''; + + $this->edittable->align["$category->id.qcount"] = "center"; + $row["$category->id.qcount"] = $category->questioncount; + + $this->edittable->align["$category->id.publish"] = "center"; + $this->edittable->wrap["$category->id.publish"] = "nowrap"; + if (!empty($category->publish)) { + $row["$category->id.publish"] = '' .$this->str->hide. ' '; + } else { + $row["$category->id.publish"] = '' .$this->str->publish. ' '; + } + + if ($category->id != $this->defaultcategory->id) { + $this->edittable->align["$category->id.delete"] = "center"; + $this->edittable->wrap["$category->id.delete"] = "nowrap"; + $row["$category->id.delete"] = '' .$this->str->delete. ' '; + } else { + $row["$category->id.delete"] = ''; + } + + $this->edittable->align["$category->id.order"] = "left"; + $this->edittable->wrap["$category->id.order"] = "nowrap"; + $icons = ''; + if ($up) { + $icons .= ' + ' . $this->str->moveup. ' '; + } + if ($down) { + $icons .= ' + ' .$this->str->movedown. ' '; + } + $row["$category->id.order"]= $icons; + + $this->edittable->align["$category->id.moveto"] = "left"; + $this->edittable->wrap["$category->id.moveto"] = "nowrap"; + if ($category->id != $this->defaultcategory->id) { + $viableparents = $this->categorystrings; + $this->set_viable_parents($viableparents, $category); + $viableparents = array(0=>$this->str->top) + $viableparents; + + $row["$category->id.moveto"] = popup_form ("category.php?id={$this->course->id}&move={$category->id}&sesskey=$USER->sesskey&moveto=", + $viableparents, "moveform{$category->id}", "$category->parent", "", "", "", true); + } else { + $row["$category->id.moveto"]='---'; + } + + + $this->edittable->data[$category->id] = $row; + } + + + function edit_single_category($categoryid) { + /// Interface for adding a new category + global $USER; + $this->initialize(); + + /// Interface for editing existing categories + if ($category = get_record("quiz_categories", "id", $categoryid)) { + echo '

'; + echo $this->str->edit; + helpbutton("categories_edit", $this->str->editcategory, "quiz"); + echo '

'; + echo '
'; + $this->output_edit_single_table($category); + echo '
'; + echo '

+ + +

'; + print_footer($this->course); + exit; + } else { + error("Category $categoryid not found", "category.php?id={$this->course->id}"); + } + } + + +/** +* Outputs a table to allow editing of an existing category +* +* @param object category +*/ + function output_edit_single_table($category) { + global $USER; + $publishoptions[0] = get_string("no"); + $publishoptions[1] = get_string("yes"); + $strupdate = get_string('update'); + + unset ($edittable); + + $edittable->head = array ($this->str->parent, $this->str->category, $this->str->categoryinfo, $this->str->publish, $this->str->action); + $edittable->width = 200; + $edittable->data[] = array(); + $edittable->tablealign = 'center'; + + /// Each section below adds a data cell to the table row + + $viableparents = $this->categorystrings; + $this->set_viable_parents($viableparents, $category); + $viableparents = array(0=>$this->str->top) + $viableparents; + $edittable->align['parent'] = "left"; + $edittable->wrap['parent'] = "nowrap"; + $row['parent'] = choose_from_menu ($viableparents, "updateparent", "{$category->parent}", "", "", "", true); + + $edittable->align['category'] = "left"; + $edittable->wrap['category'] = "nowrap"; + $row['category'] = ''; + + $edittable->align['info'] = "left"; + $edittable->wrap['info'] = "nowrap"; + $row['info'] = ''; + + $edittable->align['publish'] = "left"; + $edittable->wrap['publish'] = "nowrap"; + $selected = (boolean)$category->publish ? 1 : 0; + $row['publish'] = choose_from_menu ($publishoptions, "updatepublish", $selected, "", "", "", true); + + $edittable->align['action'] = "left"; + $edittable->wrap['action'] = "nowrap"; + $row['action'] = ''; + + $edittable->data[] = $row; + + // wrap the table in a form and output it + echo '

'; + echo "sesskey\" />"; + echo ''; + echo ''; + print_table($edittable); + echo '

'; + } + + +/** +* Creates an array of "full-path" category strings +* Structure: +* key => string +* where key is the category id, and string contains the name of all ancestors as well as the particular category name +* E.g. '123'=>'Language / English / Grammar / Modal Verbs" +* +* @param array $categories an array containing categories arranged in a tree structure +*/ + function expanded_category_strings($categories, $parent=null) { + $prefix = is_null($parent) ? '' : "$parent / "; + $categorystrings = array(); + foreach ($categories as $key => $category) { + $expandedname = "$prefix$category->name"; + $categorystrings[$key] = $expandedname; + if (isset($category->children)) { + $categorystrings = $categorystrings + $this->expanded_category_strings($category->children, $expandedname); + } + } + return $categorystrings; + } + + +/** +* Arranges the categories into a hierarchical tree +* +* If a category has children, it's "children" property holds an array of children +* The questioncount for each category is also calculated +* +* @param array records a flat list of the categories +* @return array categorytree a hierarchical list of the categories +*/ + function arrange_categories($records) { + //todo: get the question count for all records with one sql statement: select category, count(*) from quiz_questions group by category + $levels = array(); + + // build a levels array, which places each record according to it's depth from the top level + $parents = array(0); + while (!empty($parents)) { + $children = array(); + foreach ($records as $record) { + if (in_array($record->parent, $parents)) { + $children[] = $record->id; + } + } + if (!empty($children)) { + $levels[] = $children; + } + $parents = $children; + } + // if there is no hierarchy (e.g., if all records have parent == 0), set level[0] to these keys + if (empty($levels)) { + $levels[0] = array_keys($records); + } + + // build a hierarchical array that depicts the parent-child relationships of the categories + $categorytree = array(); + for ($index = count($levels) - 1; $index >= 0; $index--) { + foreach($levels[$index] as $key) { + $parentkey = $records[$key]->parent; + if (!($records[$key]->questioncount = count_records('quiz_questions', 'category', $records[$key]->id, 'hidden', 0, 'parent', '0'))) { + $records[$key]->questioncount = 0; + } + if ($parentkey == 0) { + $categorytree[$key] = $records[$key]; + } else { + $records[$parentkey]->children[$key] = $records[$key]; + } + } + } + return $categorytree; + } + + +/** +* Sets the viable parents +* +* Viable parents are any except for the category itself, or any of it's descendants +* The parentstrings parameter is passed by reference and changed by this function. +* +* @param array parentstrings a list of parentstrings +* @param object category +*/ + function set_viable_parents(&$parentstrings, $category) { + + unset($parentstrings[$category->id]); + if (isset($category->children)) { + foreach ($category->children as $child) { + $this->set_viable_parents($parentstrings, $child); + } + } + } + +/** +* Gets quiz categories +* +* @param int parent - if given, restrict records to those with this parent id. +* @param string sort - [[sortfield [,sortfield]] {ASC|DESC}] +* @return array categories +*/ + function get_quiz_categories($parent=null, $sort="sortorder ASC") { + + if (is_null($parent)) { + $categories = get_records('quiz_categories', 'course', "{$this->course->id}", $sort); + } else { + $select = "parent = '$parent' AND course = '{$this->course->id}'"; + $categories = get_records_select('quiz_categories', $select, $sort); + } + return $categories; + } + + +/** +* Deletes an existing quiz category +* +* @param int deletecat id of category to delete +* @param int destcategoryid id of category which will inherit the orphans of deletecat +*/ + function delete_category($deletecat, $destcategoryid = null) { + global $USER; + + if (!$category = get_record("quiz_categories", "id", $deletecat)) { // security + error("No such category $deletecat!", "category.php?id={$this->course->id}"); + } + + if (!is_null($destcategoryid)) { // Need to move some questions before deleting the category + if (!$category2 = get_record("quiz_categories", "id", $destcategoryid)) { // security + error("No such category $destcategoryid!", "category.php?id={$this->course->id}"); + } + if (! set_field('quiz_questions', 'category', $category2, 'category', $category1)) { + error("Error while moving questions from category '$category->name' to '$category2->name'", "category.php?id={$this->course->id}"); + } + + } else { + // todo: delete any hidden questions that are not actually in use any more + if ($count = count_records("quiz_questions", "category", $category->id)) { + $vars->name = $category->name; + $vars->count = $count; + print_simple_box(get_string("categorymove", "quiz", $vars), "center"); + $this->initialize(); + $categorystrings = $this->categorystrings; + unset ($categorystrings[$category->id]); + echo "

"; + echo "sesskey\" />"; + echo "course->id}\" />"; + echo "id\" />"; + choose_from_menu($categorystrings, "confirm", "", ""); + echo ""; + echo "str->cancel}\" />"; + echo "

"; + print_footer($this->course); + exit; + } + } + delete_records("quiz_categories", "id", $category->id); + + /// Send the children categories to live with their grandparent + if ($childcats = get_records("quiz_categories", "parent", $category->id)) { + foreach ($childcats as $childcat) { + if (! set_field("quiz_categories", "parent", $category->parent, "id", $childcat->id)) { + error("Could not update a child category!", "category.php?id={$this->course->id}"); + } + } + } + + /// Finally delete the category itself + if (delete_records("quiz_categories", "id", $category->id)) { + notify(get_string("categorydeleted", "quiz", $category->name), 'green'); + } + } + +/** +* Moves a category up or down in the display order +* +* @param string direction up|down +* @param int categoryid id of category to move +*/ + function move_category_up_down ($direction, $categoryid) { + /// Move a category up or down + $swapcategory = NULL; + $movecategory = NULL; + + if ($direction == 'up') { + if ($movecategory = get_record("quiz_categories", "id", $categoryid)) { + $categories = $this->get_quiz_categories("$movecategory->parent", 'parent, sortorder, name'); + + foreach ($categories as $category) { + if ($category->id == $movecategory->id) { + break; + } + $swapcategory = $category; + } + } + } + if ($direction == 'down') { + if ($movecategory = get_record("quiz_categories", "id", $categoryid)) { + $categories = $this->get_quiz_categories("$movecategory->parent", 'parent, sortorder, name'); + $choosenext = false; + foreach ($categories as $category) { + if ($choosenext) { + $swapcategory = $category; + break; + } + if ($category->id == $movecategory->id) { + $choosenext = true; + } + } + } + } + if ($swapcategory and $movecategory) { // Renumber everything for robustness + $count=0; + foreach ($categories as $category) { + $count++; + if ($category->id == $swapcategory->id) { + $category = $movecategory; + } else if ($category->id == $movecategory->id) { + $category = $swapcategory; + } + if (! set_field("quiz_categories", "sortorder", $count, "id", $category->id)) { + notify("Could not update that category!"); + } + } + } + } + +/** +* Changes the parent of a category +* +* @param int categoryid +* @param int parentid +*/ + function move_category($categoryid, $parentid) { + /// Move a category to a new parent + + if ($tempcat = get_record("quiz_categories", "id", $categoryid)) { + if ($tempcat->parent != $parentid) { + if (! set_field("quiz_categories", "parent", $parentid, "id", $tempcat->id)) { + notify("Could not update that category!"); + } + } + } + } + +/** +* Changes the published status of a category +* +* @param boolean publish +* @param int categoryid +*/ + function publish_category($publish, $categoryid) { + /// Hide or publish a category + + $publish = ($publish == false) ? 0 : 1; + $tempcat = get_record("quiz_categories", "id", $categoryid); + if ($tempcat) { + if (! set_field("quiz_categories", "publish", $publish, "id", $tempcat->id)) { + notify("Could not update that category!"); + } + } + } + +/** +* Creates a new category with given params +* +* @param int $newparent id of the parent category +* @param string $newcategory the name for the new category +* @param string $newinfo the info field for the new category +* @param int $newpublish whether to publish the category +* @param int $newcourse the id of the associated course +*/ + function add_category($newparent, $newcategory, $newinfo, $newpublish, $newcourse) { + + if ($newparent) { + // first check that the parent category is in the correct course + if(!(get_field('quiz_categories', 'course', 'id', $newparent) == $newcourse)) { + return false; + } + } + + $cat = NULL; + $cat->parent = $newparent; + $cat->name = $newcategory; + $cat->info = $newinfo; + $cat->publish = $newpublish; + $cat->course = $newcourse; + $cat->sortorder = 999; + $cat->stamp = make_unique_id_code(); + if (!insert_record("quiz_categories", $cat)) { + error("Could not insert the new quiz category '$newcategory'", "category.php?id={$newcourse}"); + } else { + notify(get_string("categoryadded", "quiz", $newcategory), 'green'); + } + + } + +/** +* Updates an existing category with given params +* +* @param int updateid +* @param int updateparent +* @param string updatename +* @param string updateinfo +* @param int updatepublish +* @param int courseid the id of the associated course +*/ + function update_category($updateid, $updateparent, $updatename, $updateinfo, $updatepublish, $courseid) { + + $cat = NULL; + $cat->id = $updateid; + $cat->parent = $updateparent; + $cat->name = $updatename; + $cat->info = $updateinfo; + $cat->publish = $updatepublish; + if (!update_record("quiz_categories", $cat)) { + error("Could not update the category '$updatename'", "category.php?id={$courseid}"); + } else { + notify(get_string("categoryupdated", 'quiz'), 'green'); + } + } + +} + +?>