diff --git a/mod/quiz/category.php b/mod/quiz/category.php deleted file mode 100644 index ef2aeebf32a..00000000000 --- a/mod/quiz/category.php +++ /dev/null @@ -1,126 +0,0 @@ -moveup = optional_param('moveup',0,PARAM_INT); - $param->movedown = optional_param('movedown',0,PARAM_INT); - $param->hide = optional_param('hide',0,PARAM_INT); - $param->delete = optional_param('delete',0,PARAM_INT); - $param->confirm = optional_param('confirm',0,PARAM_INT); - $param->cancel = optional_param('cancel','',PARAM_ALPHA); - $param->move = optional_param('move',0,PARAM_INT); - $param->moveto = optional_param('moveto',0,PARAM_INT); - $param->publish = optional_param('publish',0,PARAM_INT); - $param->addcategory = optional_param('addcategory','',PARAM_ALPHA); - $param->edit = optional_param('edit',0,PARAM_INT); - $param->updateid = optional_param('updateid',0,PARAM_INT); - $param->page = optional_param('page',1,PARAM_INT); - - if (! $course = get_record("course", "id", $id)) { - error("Course ID is incorrect"); - } - - if (isset($_REQUEST['backtoquiz'])) { - redirect("edit.php"); - } - - require_login($course->id, false); - - if (!isteacheredit($course->id)) { - error("Only teachers authorized to edit the course '{$course->fullname}' can use this page!"); - } - - $qcobject = new quiz_category_object(); - $qcobject->set_course($course); - - //========== - // 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')); - } - - //========== - // ACTIONS - //========== - - if (isset($_REQUEST['sesskey']) and confirm_sesskey()) { // sesskey must be ok - if (!empty($param->delete) and empty($param->cancel)) { - if (!empty($param->confirm)) { - /// 'confirm' is the category to move existing questions to - $qcobject->delete_category($param->delete, $param->confirm); - } else { - $qcobject->delete_category($param->delete); - } - } else if (!empty($param->moveup)) { - $qcobject->move_category_up_down('up', $param->moveup); - } else if (!empty($param->movedown)) { - $qcobject->move_category_up_down('down', $param->movedown); - } else if (!empty($param->hide)) { - $qcobject->publish_category(false, $param->hide); - } else if (!empty($param->move) and !empty($param->moveto)) { - $qcobject->move_category($param->move, $param->moveto); - } else if (!empty($param->publish)) { - $qcobject->publish_category(true, $param->publish); - } else if (!empty($param->addcategory)) { - $param->newparent = required_param('newparent',PARAM_INT); - $param->newcategory = required_param('newcategory',PARAM_ALPHANUM); - $param->newinfo = required_param('newinfo',PARAM_CLEAN); - $param->newpublish = required_param('newpublish',PARAM_INT); - $qcobject->add_category($param->newparent, $param->newcategory, $param->newinfo, - $param->newpublish, $course->id); - } else if (!empty($param->edit)) { - $qcobject->edit_single_category($param->edit, $param->page); - } else if (!empty($param->updateid)) { - $param->updateparent = required_param('updateparent',PARAM_INT); - $param->updatename = required_param('updatename',PARAM_ALPHANUM); - $param->updateinfo = required_param('updateinfo',PARAM_CLEAN); - $param->updatepublish = required_param('updatepublish',PARAM_INT); - $qcobject->update_category($param->updateid, $param->updateparent, $param->updatename, - $param->updateinfo, $param->updatepublish, $course->id); - } - } - - //========== - // DISPLAY - //========== - - // display the user interface - $qcobject->display_user_interface($param->page); - - print_footer($course); - - - - -?> diff --git a/mod/quiz/category_class.php b/mod/quiz/category_class.php deleted file mode 100644 index e227a60bcd2..00000000000 --- a/mod/quiz/category_class.php +++ /dev/null @@ -1,773 +0,0 @@ -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->str->page = get_string('page'); - $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 -* @param int $page page number to display (0=don't paginate) -*/ - function display_user_interface($page=0) { - $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($page); - if ($this->topcount>PAGE_LENGTH) { - $this->display_page_numbers($page); - } - 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); - - // for pagination calculate number of 'top' categories and hence number of pages - // (pagination only based on top categories) - $count = 0; - foreach( $this->categories as $category ) { - if ($category->parent==0) { - ++$count; - } - } - $this->topcount = $count; - $this->pagecount = (integer) ceil( $count / PAGE_LENGTH ); - } - -/** - * display list of page numbers for navigation - */ - function display_page_numbers( $page=0 ) { - global $USER; - - echo "
{$this->str->page}:\n"; - foreach (range(1,$this->pagecount) as $currentpage) { - if ($page == $currentpage) { - echo " $currentpage \n"; - } - else { - echo "course->id}&page=$currentpage&sesskey={$USER->sesskey}\">"; - echo " $currentpage \n"; - } - } - echo "
"; - } - -/** -* 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 -* @param int $page page to display (0=do not paginate) -*/ - function output_edit_table($page=0) { - $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; - - // if pagination required work out range - if (!empty($page)) { - $firstcat = ($page-1) * PAGE_LENGTH + 1; - $lastcat = $firstcat + PAGE_LENGTH - 1; - } - else { - $firstcat = 1; - $lastcat = $this->topcount; - } -//echo "$firstcat $lastcat $page"; die; - $this->build_edit_table_body($this->categories, $page, $firstcat, $lastcat); - 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, $page = 0, $firstcat = 1, $lastcat = 99999, $depth = 0) { - $countcats = count($categories); - $count = 0; - $first = true; - $last = false; - $topcount = 0; - - foreach ($categories as $category) { - $count++; - if ($count == $countcats) { - $last = true; - } - // check if this category is on the display page - if ($depth==0) { - $topcount++; - if (($topcount<$firstcat) or ($topcount>$lastcat)) { - continue; - } - } - $up = $first ? false : true; - $down = $last ? false : true; - $first = false; - $this->quiz_edit_category_row($category, $depth, $up, $down, $page); - if (isset($category->children)) { - $this->build_edit_table_body($category->children, $page, $firstcat, $lastcat, $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? -* @param int page page number -*/ - function quiz_edit_category_row($category, $depth, $up = false, $down = false, $page = 0) { - global $USER; - $fill = str_repeat($this->tab, $depth); - - $linkcss = $category->publish ? ' class="published"' : ' class="unpublished"'; - - if (!empty($page)) { - $pagelink="&page=$page"; - } - else { - $pagelink=""; - } - - /// 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$pagelink&moveto=", - $viableparents, "moveform{$category->id}", "$category->parent", "", "", "", true); - } else { - $row["$category->id.moveto"]='---'; - } - - - $this->edittable->data[$category->id] = $row; - } - - - function edit_single_category($categoryid,$page=1) { - /// 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,$page); - 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 -* @param int page current page -*/ - function output_edit_single_table($category, $page=1) { - 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 ''; - 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/constants.php b/mod/quiz/constants.php index 807fda24135..bca1ae20c6c 100644 --- a/mod/quiz/constants.php +++ b/mod/quiz/constants.php @@ -64,43 +64,6 @@ define('QUIZ_REVIEW_SOLUTIONS', 16*4161); // Show solutions // the 6th bit is as yet unused /**#@-*/ -/**#@+ -* The different types of events that can create question states -*/ -define('QUIZ_EVENTOPEN', '0'); -define('QUIZ_EVENTNAVIGATE', '1'); -define('QUIZ_EVENTSAVE', '2'); -define('QUIZ_EVENTGRADE', '3'); -define('QUIZ_EVENTDUPLICATEGRADE', '4'); -define('QUIZ_EVENTVALIDATE', '5'); -define('QUIZ_EVENTCLOSE', '6'); -define('QUIZ_EVENTSUBMIT', '7'); -/**#@-*/ - -/**#@+ -* The defined question types -* -* @todo It would be nicer to have a fully automatic plug-in system -*/ -define("SHORTANSWER", "1"); -define("TRUEFALSE", "2"); -define("MULTICHOICE", "3"); -define("RANDOM", "4"); -define("MATCH", "5"); -define("RANDOMSAMATCH", "6"); -define("DESCRIPTION", "7"); -define("NUMERICAL", "8"); -define("MULTIANSWER", "9"); -define("CALCULATED", "10"); -define("RQP", "11"); -define("ESSAY", "12"); -/**#@-*/ - -define("QUIZ_MAX_NUMBER_ANSWERS", "10"); - -define("QUIZ_CATEGORIES_SORTORDER", "999"); - - /** * If start and end date for the quiz are more than this many seconds apart * they will be represented by two separate events in the calendar diff --git a/mod/quiz/edit.php b/mod/quiz/edit.php index 2441ae698c8..02ce9f1faca 100644 --- a/mod/quiz/edit.php +++ b/mod/quiz/edit.php @@ -34,18 +34,12 @@ * @package quiz */ require_once("../../config.php"); - require_once("editlib.php"); + require_once($CFG->dirroot.'/mod/quiz/editlib.php'); require_login(); $courseid = optional_param('courseid'); $quizid = optional_param('quizid'); - $page = optional_param('page', -1); - $perpage = optional_param('perpage', 20); - $sortorder = optional_param('sortorder', 'qtype, name ASC'); - if (preg_match("/[';]/", $sortorder)) { - error("Incorrect use of the parameter 'sortorder'"); - } $strquizzes = get_string('modulenameplural', 'quiz'); $strquiz = get_string('modulename', 'quiz'); @@ -129,12 +123,6 @@ if (self.name == 'editquestion') { $modform->grades = quiz_get_all_question_grades($modform); } - if ($page > -1) { - $modform->page = $page; - } else { - $page = isset($modform->page) ? $modform->page : 0; - } - /// Now, check for commands on this page and modify variables as necessary if (isset($_REQUEST['up']) and confirm_sesskey()) { /// Move the given question up a slot @@ -250,86 +238,10 @@ if (self.name == 'editquestion') { } } - if (isset($_REQUEST['move']) and confirm_sesskey()) { /// Move selected questions to new category - if (!$tocategory = get_record('quiz_categories', 'id', $_REQUEST['category'])) { - error('Invalid category'); - } - if (!isteacheredit($tocategory->course)) { - error(get_string('categorynoedit', 'quiz', $tocategory->name), 'edit.php'); - } - foreach ($_POST as $key => $value) { // Parse input for question ids - if (substr($key, 0, 1) == "q") { - $key = substr($key,1); - if (!set_field('quiz_questions', 'category', $tocategory->id, 'id', $key)) { - error('Could not update category field'); - } - } - } - } - if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz quiz_delete_quiz_question($_REQUEST['delete'], $modform); } - if (isset($_REQUEST['deleteselected'])) { // delete selected questions from the category - - if (isset($confirm) and confirm_sesskey()) { // teacher has already confirmed the action - if ($confirm == md5($deleteselected)) { - if ($questionlist = explode(',', $deleteselected)) { - // for each question either hide it if it is in use or delete it - foreach ($questionlist as $questionid) { - if (record_exists('quiz_question_instances', 'question', $questionid) or - record_exists('quiz_states', 'originalquestion', $questionid)) { - if (!set_field('quiz_questions', 'hidden', 1, 'id', $questionid)) { - error('Was not able to hide question'); - } - } else { - delete_records("quiz_questions", "id", $questionid); - } - } - } - redirect("edit.php"); - } else { - error("Confirmation string was incorrect"); - } - - } else { // teacher still has to confirm - // make a list of all the questions that are selected - $rawquestions = $_POST; - $questionlist = ''; // comma separated list of ids of questions to be deleted - $questionnames = ''; // string with names of questions separated by
with - // an asterix in front of those that are in use - $inuse = false; // set to true if at least one of the questions is in use - foreach ($rawquestions as $key => $value) { // Parse input for question ids - if (substr($key, 0, 1) == "q") { - $key = substr($key,1); - $questionlist .= $key.','; - if (record_exists('quiz_question_instances', 'question', $key) or - record_exists('quiz_states', 'originalquestion', $key)) { - $questionnames .= '* '; - $inuse = true; - } - $questionnames .= get_field('quiz_questions', 'name', 'id', $key).'
'; - } - } - if (!$questionlist) { // no questions were selected - redirect('edit.php'); - } - $questionlist = rtrim($questionlist, ','); - - // Add an explanation about questions in use - if ($inuse) { - $questionnames .= get_string('questionsinuse', 'quiz'); - } - print_header_simple($streditingquestions, '', - "$streditingquestions"); - notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames), - "edit.php?sesskey=$USER->sesskey&deleteselected=$questionlist&confirm=".md5($questionlist), "edit.php"); - print_footer($course); - exit; - } - } - if (isset($_REQUEST['savechanges']) and confirm_sesskey()) { /// We need to save the new ordering (if given) and the new grades $oldquestions = explode(",", $modform->questions); // the questions in the old order @@ -371,22 +283,10 @@ if (self.name == 'editquestion') { } } - if (isset($_REQUEST['cat'])) { /// coming from category selection drop-down menu - $modform->category = $cat; - $page = 0; - $modform->page = 0; - } - - if(isset($_REQUEST['recurse'])) { - $SESSION->quiz_recurse = optional_param('recurse', 0, PARAM_BOOL); - } if(isset($_REQUEST['showbreaks'])) { $SESSION->quiz_showbreaks = optional_param('showbreaks', 0, PARAM_BOOL); $SESSION->quiz_reordertool = optional_param('reordertool', 0, PARAM_BOOL); } - if(isset($_REQUEST['showhidden'])) { - $SESSION->quiz_showhidden = optional_param('showhidden', 0, PARAM_BOOL); - } /// Delete any teacher preview attempts if the quiz has been modified if (isset($_REQUEST['setgrades']) or isset($_REQUEST['delete']) or isset($_REQUEST['repaginate']) or isset($_REQUEST['addrandom']) or isset($_REQUEST['addquestion']) or isset($_REQUEST['up']) or isset($_REQUEST['down']) or isset($_REQUEST['add'])) { @@ -399,12 +299,6 @@ if (self.name == 'editquestion') { $category = quiz_get_default_category($course->id); $modform->category = $category->id; } - if (!isset($SESSION->quiz_recurse)) { - $SESSION->quiz_recurse = 1; - } - if (!isset($SESSION->quiz_showhidden)) { - $SESSION->quiz_showhidden = false; - } if (!isset($SESSION->quiz_showbreaks)) { $SESSION->quiz_showbreaks = ($CFG->quiz_questionsperpage < 2) ? 0 : 1; } @@ -498,18 +392,7 @@ if (self.name == 'editquestion') { echo ''; } - // non-quiz-specific column - print_simple_box_start("center", "100%"); - // starts with category selection form - quiz_print_category_form($course, $modform->category, $SESSION->quiz_recurse, $SESSION->quiz_showhidden); - print_simple_box_end(); - - print_spacer(5,1); - // continues with list of questions - print_simple_box_start("center", "100%"); - quiz_print_cat_question_list($course, $modform->category, - isset($modform->instance) ? $modform->instance : 0, $SESSION->quiz_recurse, $page, $perpage, $SESSION->quiz_showhidden, $sortorder); - print_simple_box_end(); + require($CFG->dirroot.'/question/showbank.php'); echo ''; echo ''; diff --git a/mod/quiz/editlib.php b/mod/quiz/editlib.php index 6f1ab4fa121..62fcb7b28b6 100644 --- a/mod/quiz/editlib.php +++ b/mod/quiz/editlib.php @@ -12,33 +12,6 @@ require_once("locallib.php"); -/** -* Array of question types names translated to the user's language -* -* The $QUIZ_QUESTION_TYPE array holds the names of all the question types that the user should -* be able to create directly. Some internal question types like random questions are excluded. -* The complete list of question types can be found in {@link $QUIZ_QTYPES}. -*/ - -$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"), - TRUEFALSE => get_string("truefalse", "quiz"), - SHORTANSWER => get_string("shortanswer", "quiz"), - NUMERICAL => get_string("numerical", "quiz"), - CALCULATED => get_string("calculated", "quiz"), - MATCH => get_string("match", "quiz"), - DESCRIPTION => get_string("description", "quiz"), - RANDOMSAMATCH => get_string("randomsamatch", "quiz"), - MULTIANSWER => get_string("multianswer", "quiz"), - ESSAY => get_string("essay", "quiz") - ); -// add remote question types -if ($rqp_types = get_records('quiz_rqp_types')) { - foreach($rqp_types as $type) { - $QUIZ_QUESTION_TYPE[100+$type->id] = $type->name; - } -} - - /** * Delete a question from a quiz * @@ -286,10 +259,10 @@ function quiz_print_question_list($quiz, $allowdelete=true, $showbreaks=true, $r $context = $quiz->id ? '&contextquiz='.$quiz->id : ''; $quiz_id = $quiz->id ? '&quizid=' . $quiz->id : ''; - echo " + echo " pixpath/t/preview.gif\" border=\"0\" alt=\"$strpreview\" />"; if ($canedit) { - echo " + echo "wwwroot/question/question.php?id=$qnum$context\"> pixpath/t/edit.gif\" border=\"0\" alt=\"$stredit\" />"; } if ($allowdelete) { @@ -360,269 +333,4 @@ function quiz_print_question_list($quiz, $allowdelete=true, $showbreaks=true, $r return $sumgrade; } -function quiz_print_category_form($course, $current, $recurse=1, $showhidden=false) { -/// Prints a form to choose categories - -/// Make sure the default category exists for this course - if (!$categories = get_records("quiz_categories", "course", $course->id, "id ASC")) { - if (!$category = quiz_get_default_category($course->id)) { - notify("Error creating a default category!"); - } - } - -/// Get all the existing categories now - if (!$categories = get_records_select("quiz_categories", "course = '{$course->id}' OR publish = '1'", "parent, sortorder, name ASC")) { - notify("Could not find any question categories!"); - return false; // Something is really wrong - } - $categories = add_indented_names($categories); - foreach ($categories as $key => $category) { - if ($catcourse = get_record("course", "id", $category->course)) { - if ($category->publish && $category->course != $course->id) { - $category->indentedname .= " ($catcourse->shortname)"; - } - $catmenu[$category->id] = $category->indentedname; - } - } - $strcategory = get_string("category", "quiz"); - $strshow = get_string("show", "quiz"); - $streditcats = get_string("editcategories", "quiz"); - - echo "
"; - echo "$strcategory: "; - echo ""; - popup_form ("edit.php?cat=", $catmenu, "catmenu", $current, "", "", "", false, "self"); - echo ""; - echo "
"; - echo "id\" />"; - echo ""; - echo "
"; - echo '
'; - echo '
'; - echo '
'; - echo ''; - echo ''; - print_string('recurse', 'quiz'); - // hide-feature - echo '
'; - echo ''; - echo ''; - print_string('showhidden', 'quiz'); - echo '
'; - echo ' '; - echo '
'; -} - - -/** -* Prints the table of questions in a category with interactions -* -* @param object $course The course object -* @param int $categoryid The id of the question category to be displayed -* @param int $quizid The quiz id if we are in the context of a particular quiz, 0 otherwise -* @param int $recurse This is 1 if subcategories should be included, 0 otherwise -* @param int $page The number of the page to be displayed -* @param int $perpage Number of questions to show per page -* @param boolean $showhidden True if also hidden questions should be displayed -*/ -function quiz_print_cat_question_list($course, $categoryid, $quizid, - $recurse=1, $page, $perpage, $showhidden=false, $sortorder='qtype, name ASC') { - global $QUIZ_QUESTION_TYPE, $USER, $CFG; - - $strcategory = get_string("category", "quiz"); - $strquestion = get_string("question", "quiz"); - $straddquestions = get_string("addquestions", "quiz"); - $strimportquestions = get_string("importquestions", "quiz"); - $strexportquestions = get_string("exportquestions", "quiz"); - $strnoquestions = get_string("noquestions", "quiz"); - $strselect = get_string("select", "quiz"); - $strselectall = get_string("selectall", "quiz"); - $strselectnone = get_string("selectnone", "quiz"); - $strcreatenewquestion = get_string("createnewquestion", "quiz"); - $strquestionname = get_string("questionname", "quiz"); - $strdelete = get_string("delete"); - $stredit = get_string("edit"); - $straction = get_string("action"); - $strrestore = get_string('restore'); - - $straddtoquiz = get_string("addtoquiz", "quiz"); - $strtype = get_string("type", "quiz"); - $strcreatemultiple = get_string("createmultiple", "quiz"); - $strpreview = get_string("preview","quiz"); - - if (!$categoryid) { - echo "

"; - print_string("selectcategoryabove", "quiz"); - echo "

"; - if ($quizid) { - echo "

"; - print_string("addingquestions", "quiz"); - echo "

"; - } - return; - } - - if (!$category = get_record("quiz_categories", "id", "$categoryid")) { - notify("Category not found!"); - return; - } - echo "
"; - echo format_text($category->info, FORMAT_MOODLE); - - echo ''; - - // check if editing of this category is allowed - if (isteacheredit($category->course)) { - echo ""; - echo ''; - } - else { - echo ''; - } - - echo ''; - - echo '
$strcreatenewquestion:'; - popup_form ("question.php?category=$category->id&qtype=", $QUIZ_QUESTION_TYPE, "addquestion", - "", "choose", "", "", false, "self"); - echo ''; - helpbutton("questiontypes", $strcreatenewquestion, "quiz"); - echo '
'; - print_string("publishedit","quiz"); - echo '
'; - if (isteacheredit($category->course)) { - echo ''.$strimportquestions.''; - helpbutton("import", $strimportquestions, "quiz"); - echo ' | '; - } - echo "id}&courseid={$course->id}\">$strexportquestions"; - helpbutton("export", $strexportquestions, "quiz"); - echo '
'; - - echo '
'; - - $categorylist = ($recurse) ? quiz_categorylist($category->id) : $category->id; - - // hide-feature - $showhidden = $showhidden ? '' : " AND hidden = '0'"; - - if (!$totalnumber = count_records_select('quiz_questions', "category IN ($categorylist) AND parent = '0' $showhidden")) { - echo "

"; - print_string("noquestions", "quiz"); - echo "

"; - return; - } - - if (!$questions = get_records_select('quiz_questions', "category IN ($categorylist) AND parent = '0' $showhidden", $sortorder, '*', $page*$perpage, $perpage)) { - // There are no questions on the requested page. - $page = 0; - if (!$questions = get_records_select('quiz_questions', "category IN ($categorylist) AND parent = '0' $showhidden", $sortorder, '*', 0, $perpage)) { - // There are no questions at all - echo "

"; - print_string("noquestions", "quiz"); - echo "

"; - return; - } - } - - print_paging_bar($totalnumber, $page, $perpage, - "edit.php?perpage=$perpage&"); - - $canedit = isteacheredit($category->course); - - echo '
'; - echo ''; - print_simple_box_start('center', '100%', '#ffffff', 0); - echo ''; - $actionwidth = $canedit ? 95 : 70; - echo ""; - - $sortoptions = array('name, qtype ASC' => get_string("sortalpha", "quiz"), - 'qtype, name ASC' => get_string("sorttypealpha", "quiz"), - 'id ASC' => get_string("sortage", "quiz")); - $orderselect = choose_from_menu ($sortoptions, 'sortorder', $sortorder, false, 'this.form.submit();', '0', true); - $orderselect .= ''; - echo " - "; - echo "\n"; - foreach ($questions as $question) { - if ($question->qtype == RANDOM) { - //continue; - } - echo "\n\n"; - - if ($question->hidden) { - echo '\n"; - } else { - echo "\n"; - } - echo "\n"; - echo "\n"; - } - echo '
$straction$strquestionname $orderselect$strtype
\n"; - if ($quizid) { - echo "id&sesskey=$USER->sesskey\">pixpath/t/moveleft.gif\" border=\"0\" alt=\"$straddtoquiz\" /> "; - } - echo "id&quizid=$quizid','$strpreview','scrollbars=yes,resizable=yes,width=700,height=480', false)\">pixpath/t/preview.gif\" border=\"0\" alt=\"$strpreview\" /> "; - if ($canedit) { - echo "id\">pixpath/t/edit.gif\" border=\"0\" alt=\"$stredit\" /> "; - // hide-feature - if($question->hidden) { - echo "id&hide=0&sesskey=$USER->sesskey\">pixpath/t/restore.gif\" border=\"0\" alt=\"$strrestore\" />"; - } else { - echo "id&delete=$question->id\">pixpath/t/delete.gif\" border=\"0\" alt=\"$strdelete\" />"; - } - } - echo " id\" value=\"1\" />"; - echo "'.$question->name."".$question->name."\n"; - quiz_print_question_icon($question, $canedit); - echo "
'; - print_paging_bar($totalnumber, $page, $perpage, "edit.php?perpage=$perpage&"); - echo "
\n"; - print_simple_box_end(); - - echo '
'; - echo ''.$strselectall.' /'. - ' '.$strselectnone.''. - ' '.get_string('withselected', 'quiz').':
'; - if ($quizid) { - echo "\n"; - echo ''; - } - if ($canedit) { - echo '\n"; - echo '\n"; - quiz_category_select_menu($course->id, false, true, $category->id); - } - echo "
"; - - if ($quizid) { - for ($i=1;$i<=10; $i++) { - $randomcount[$i] = $i; - } - echo '
'; - print_string('addrandom', 'quiz', - choose_from_menu($randomcount, 'randomcount', '1', '', '', '', true)); - echo ''; - echo "id\" />"; - echo ' '; - helpbutton('random', get_string('random', 'quiz'), 'quiz'); - } - - echo "
\n"; -} - ?> diff --git a/mod/quiz/export.php b/mod/quiz/export.php index 210856d4096..7866808ba62 100644 --- a/mod/quiz/export.php +++ b/mod/quiz/export.php @@ -11,7 +11,7 @@ require_once("../../config.php"); require_once("locallib.php"); // TODO: this should not need locallib.php - require_once('questionlib.php'); + require_once($CFG->libdir.'/questionlib.php'); $categoryid = optional_param('category',0, PARAM_INT); $courseid = required_param('courseid',PARAM_INT); @@ -70,8 +70,7 @@ include('tabs.php'); } else { print_header_simple($strexportquestions, '', - "id\">".get_string('modulenameplural', 'quiz').''. - '-> '.get_string('editquestions', 'quiz').''. + ''.get_string('editquestions', 'quiz').''. ' -> '.$strexportquestions); } diff --git a/mod/quiz/import.php b/mod/quiz/import.php index af1443f25f5..15c850f35e0 100644 --- a/mod/quiz/import.php +++ b/mod/quiz/import.php @@ -11,7 +11,7 @@ require_once("../../config.php"); require_once("locallib.php"); // TODO: this should not need locallib.php - require_once('questionlib.php'); + require_once($CFG->libdir.'/questionlib.php'); $categoryid = optional_param('category', 0, PARAM_INT); $courseid = optional_param('course', 0, PARAM_INT); diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 1fe6ee32e68..7b169c6c875 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -20,7 +20,7 @@ * Include those library functions that are also used by core Moodle or other modules */ require_once("$CFG->dirroot/mod/quiz/lib.php"); -require_once("$CFG->dirroot/mod/quiz/questionlib.php"); +require_once($CFG->libdir.'/questionlib.php'); /// CONSTANTS /////////////////////////////////////////////////////////////////// @@ -491,7 +491,7 @@ function quiz_get_question_review($quiz, $question) { $strpreview = get_string('previewquestion', 'quiz'); $context = $quiz->id ? '&contextquiz='.$quiz->id : ''; $quiz_id = $quiz->id ? '&quizid=' . $quiz->id : ''; - return " + return " \"$strpreview\""; } diff --git a/mod/quiz/preview.php b/mod/quiz/preview.php deleted file mode 100644 index 05cb5a38bee..00000000000 --- a/mod/quiz/preview.php +++ /dev/null @@ -1,229 +0,0 @@ -quizpreview); - // Redirect to ourselves but with continue=1; prevents refreshing the page - // from restarting an attempt (needed so that random questions don't change) - $quizid = $quizid ? '&quizid=' . $quizid : ''; - redirect($CFG->wwwroot . '/mod/quiz/preview.php?id=' . $id . $quizid . - '&continue=1'); - } - - if (empty($quizid)) { - // get a sample quiz to be used as a skeleton - // this should really be done properly by instantiating a quiz object - if (!$quiz = get_records('quiz')) { - error('You have to create at least one quiz before using this preview'); - } - $quiz = array_values($quiz); - $quiz = $quiz[0]; - - // set everything to the default values - foreach($quiz as $field => $value) { - $quizfield = "quiz_".$field; - if(isset($CFG->$quizfield)) { - $quiz->$field = $CFG->$quizfield; - } - else { - $quiz->$field = 0; - } - } - - } else if (!$quiz = get_record('quiz', 'id', $quizid)) { - error("Quiz id $quizid does not exist"); - } - - // Load the question information - if (!$questions = get_records('quiz_questions', 'id', $id)) { - error('Could not load question'); - } - if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) { - $questions[$id]->maxgrade = $maxgrade; - } else { - $questions[$id]->maxgrade = $questions[$id]->defaultgrade; - } - - $quiz->id = 0; // just for safety - $quiz->questions = $id; - - if (!$category = get_record("quiz_categories", "id", $questions[$id]->category)) { - error("This question doesn't belong to a valid category!"); - } - - if (!isteacher($category->course) and !$category->publish) { - error("You can't preview these questions!"); - } - $quiz->course = $category->course; - - // Load the question type specific information - if (!quiz_get_question_options($questions)) { - error(get_string('newattemptfail', 'quiz')); - } - - // Create a dummy quiz attempt - $attempt = quiz_create_attempt($quiz, 0); - $attempt->id = 0; - - // Restore the history of question sessions from the moodle session or create - // new sessions. Make $states a reference to the states array in the moodle - // session. - if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid - == $id) { - // Reload the question session history from the moodle session - $states =& $SESSION->quizpreview->states; - $historylength = count($states) - 1; - if ($back && $historylength > 0) { - // Go back one step in the history - unset($states[$historylength]); - $historylength--; - } - } else { - // Record the question id in the moodle session - $SESSION->quizpreview->questionid = $id; - // Create an empty session for the question - if (!$newstates = - quiz_get_states($questions, $quiz, $attempt)) { - error(get_string('newattemptfail', 'quiz')); - } - $SESSION->quizpreview->states = array($newstates); - $states =& $SESSION->quizpreview->states; - $historylength = 0; - } - - if (!$fillcorrect && !$back && ($form = data_submitted())) { - $form = (array)$form; - $submitted = true; - - // Create a new item in the history of question states (don't simplify!) - $states[$historylength + 1] = array(); - $states[$historylength + 1][$id] = clone($states[$historylength][$id]); - $historylength++; - $curstate =& $states[$historylength][$id]; - - // Process the responses - unset($form['id']); - unset($form['quizid']); - unset($form['continue']); - unset($form['markall']); - unset($form['finishattempt']); - unset($form['back']); - unset($form['startagain']); - - $event = $finishattempt ? QUIZ_EVENTCLOSE : ($markall ? QUIZ_EVENTGRADE : QUIZ_EVENTSAVE); - if ($actions = quiz_extract_responses($questions, $form, $event)) { - $actions[$id]->timestamp = 0; // We do not care about timelimits here - quiz_process_responses($questions[$id], $states[$historylength][$id], $actions[$id], $quiz, $attempt); - if (QUIZ_EVENTGRADE != $curstate->event && QUIZ_EVENTCLOSE != $curstate->event) { - // Update the current state rather than creating a new one - $historylength--; - unset($states[$historylength]); - $states = array_values($states); - $curstate =& $states[$historylength][$id]; - } - } - } else { - $submitted = false; - $curstate =& $states[$historylength][$id]; - } - - $options = quiz_get_renderoptions($quiz, $curstate); - - // Fill in the correct responses (unless the question is in readonly mode) - if ($fillcorrect && !$options->readonly) { - $curstate->responses = $QUIZ_QTYPES[$questions[$id]->qtype] - ->get_correct_responses($questions[$id], $curstate); - } - - $strpreview = get_string('previewquestion', 'quiz'); - print_header($strpreview); - print_heading($strpreview); - - echo '

' . get_string('modulename', 'quiz') . ': '; - if (empty($quizid)) { - echo '[' . get_string('default', 'quiz') . ']'; - } else { - p($quiz->name); - } - echo '
'.get_string('question', 'quiz').': '; - p($questions[$id]->name); - echo "

\n"; - $number = 1; - echo "
\n"; - echo "\n"; - echo "\n"; - echo "\n"; - - quiz_print_quiz_question($questions[$id], $curstate, $number, $quiz, $options); - - echo '
'; - echo '
'; - - // Print the mark and finish attempt buttons - echo '\n"; - echo '\n"; - echo '
'; - echo '
'; - // Print the fill correct button (unless the question is in readonly mode) - if (!$options->readonly) { - echo '\n"; - } - // Print the navigation buttons - if ($historylength > 0) { - echo '\n"; - } - // Print the start again button - echo '\n"; - // Print the close window button - echo '"; - echo '
'; - echo '
'; - print_footer(); -?> diff --git a/mod/quiz/question.php b/mod/quiz/question.php deleted file mode 100644 index 7f5989215ae..00000000000 --- a/mod/quiz/question.php +++ /dev/null @@ -1,352 +0,0 @@ -libdir.'/filelib.php'); - - $id = optional_param('id'); // question id - - $qtype = optional_param('qtype'); - $category = optional_param('category'); - - // a qtype > 99 means a remote question - if ($qtype > 99) { - $typeid = $qtype - 100; - $qtype = RQP; - } - - $contextquiz = optional_param('contextquiz'); // the quiz from which this question is being edited - - if (isset($_REQUEST['cancel'])) { - redirect("edit.php"); - } - - if(!empty($id) && isset($_REQUEST['hide']) && confirm_sesskey()) { - if(!set_field('quiz_questions', 'hidden', $_REQUEST['hide'], 'id', $id)) { - error("Faild to hide the question."); - } - redirect('edit.php'); - } - - if ($id) { - if (! $question = get_record("quiz_questions", "id", $id)) { - error("This question doesn't exist"); - } - if (!empty($category)) { - $question->category = $category; - } - if (! $category = get_record("quiz_categories", "id", $question->category)) { - error("This question doesn't belong to a valid category!"); - } - if (! $course = get_record("course", "id", $category->course)) { - error("This question category doesn't belong to a valid course!"); - } - - $qtype = $question->qtype; - - - } else if ($category) { // only for creating new questions - if (! $category = get_record("quiz_categories", "id", $category)) { - error("This wasn't a valid category!"); - } - if (! $course = get_record("course", "id", $category->course)) { - error("This category doesn't belong to a valid course!"); - } - - $question->category = $category->id; - $question->qtype = $qtype; - - } else { - error("Must specify question id or category"); - } - - if (empty($qtype)) { - error("No question type was specified!"); - } else if (!isset($QUIZ_QTYPES[$qtype])) { - error("Could not find question type: '$qtype'"); - } - - require_login($course->id, false); - - if (!isteacheredit($course->id)) { - error("You can't modify these questions!"); - } - - $strquizzes = get_string('modulenameplural', 'quiz'); - $streditingquestion = get_string('editingquestion', 'quiz'); - if (isset($SESSION->modform->instance)) { - $strediting = ''.get_string('editingquiz', 'quiz').' -> '. - $streditingquestion; - } else { - $strediting = ''. - get_string("editquestions", "quiz").' -> '.$streditingquestion; - } - - print_header_simple("$streditingquestion", "", $strediting); - - if (isset($_REQUEST['delete'])) { - if (isset($confirm) and confirm_sesskey()) { - if ($confirm == md5($delete)) { - if (record_exists('quiz_question_instances', 'question', $question->id) or - record_exists('quiz_states', 'originalquestion', $question->id)) { - if (!set_field('quiz_questions', 'hidden', 1, 'id', $delete)) { - error('Was not able to hide question'); - } - } else { - if (!delete_records("quiz_questions", "id", $question->id)) { - error("An error occurred trying to delete question (id $question->id)"); - } - if (!delete_records("quiz_questions", "parent", $question->id)) { - error("An error occurred trying to delete question (id $question->id)"); - } - } - redirect("edit.php"); - } else { - error("Confirmation string was incorrect"); - } - - } else { - if ($quiznames = quizzes_question_used($id)) { - $a->questionname = $question->name; - $a->quiznames = implode(', ', $quiznames); - notify(get_string('questioninuse', 'quiz', $a)); - } - - notice_yesno(get_string("deletequestioncheck", "quiz", $question->name), - "question.php?sesskey=$USER->sesskey&id=$question->id&delete=$delete&confirm=".md5($delete), "edit.php"); - } - print_footer($course); - exit; - } - - if ($form = data_submitted() and confirm_sesskey()) { - - if (isset($form->versioning) && isset($question->id) and false) { // disable versioning until it is fixed. - // use new code that handles whether to overwrite or copy a question - // and keeps track of the versions in the quiz_question_version table - - // $replaceinquiz is an array with the ids of all quizzes in which - // the teacher has chosen to replace the old version - $replaceinquiz = array(); - foreach($form as $key => $val) { - if ($tmp = quiz_parse_fieldname($key, 'q')) { - if ($tmp['mode'] == 'replace') { - $replaceinquiz[$tmp['id']] = $tmp['id']; - unset($form->$key); - } - } - } - - // $quizlist is an array with the ids of quizzes which use this question - $quizlist = array(); - if ($instances = get_records('quiz_question_instances', 'question', $question->id)) { - foreach($instances as $instance) { - $quizlist[$instance->quiz] = $instance->quiz; - } - } - - if (isset($form->makecopy)) { // explicitly requested copies should be unhidden - $question->hidden = 0; - } - - // Logic to determine whether old version should be overwritten - $makecopy = isset($form->makecopy) || (!$form->id); unset($form->makecopy); - if ($makecopy) { - $replaceold = false; - } else { - // this should be improved to exclude teacher preview responses and empty responses - // the current code leaves many unneeded questions in the database - $hasresponses = record_exists('quiz_states', 'question', $form->id) or - record_exists('quiz_states', 'originalquestion', $form->id); - $replaceinall = ($quizlist == $replaceinquiz); // question is being replaced in all quizzes - $replaceold = !$hasresponses && $replaceinall; - } - - if (!$replaceold) { // create a new question - $oldquestionid = $question->id; - if (!$makecopy) { - if (!set_field("quiz_questions", 'hidden', 1, 'id', $question->id)) { - error("Could not hide question!"); - } - } - unset($question->id); - } - unset($makecopy, $hasresponses, $replaceinall, $replaceold); - $question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course); - if(!isset($question->id)) { - error("Failed to save the question!"); - } - - if(isset($oldquestionid)) { - // create version entries for different quizzes - $version = new object(); - $version->oldquestion = $oldquestionid; - $version->newquestion = $question->id; - $version->userid = $USER->id; - $version->timestamp = time(); - - foreach($replaceinquiz as $qid) { - $version->quiz = $qid; - if(!insert_record("quiz_question_versions", $version)) { - error("Could not store version information of question $oldquestionid in quiz $qid!"); - } - } - - /// now update the question references in the quizzes - if (!empty($replaceinquiz) and $quizzes = get_records_list("quiz", "id", implode(',', $replaceinquiz))) { - - foreach($quizzes as $quiz) { - $questionlist = ",$quiz->questions,"; // a little hack with the commas here. not nice but effective - $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist); - $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again - if (!set_field("quiz", 'questions', $questionlist, 'id', $quiz->id)) { - error("Could not update questionlist in quiz $quiz->id!"); - } - - // the quiz_question_instances table needs to be updated too (aah, the joys of duplication :) - if (!set_field('quiz_question_instances', 'question', $question->id, 'quiz', $quiz->id, 'question', $oldquestionid)) { - error("Could not update question instance!"); - } - if (isset($SESSION->modform) && (int)$SESSION->modform->instance === (int)$quiz->id) { - $SESSION->modform->questions = $questionlist; - $SESSION->modform->grades[$question->id] = $SESSION->modform->grades[$oldquestionid]; - unset($SESSION->modform->grades[$oldquestionid]); - } - } - - // change question in attempts - if ($attempts = get_records_list('quiz_attempts', 'quiz', implode(',', $replaceinquiz))) { - foreach ($attempts as $attempt) { - - // replace question id in $attempt->layout - $questionlist = ",$attempt->layout,"; // a little hack with the commas here. not nice but effective - $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist); - $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again - if (!set_field('quiz_attempts', 'layout', $questionlist, 'id', $attempt->id)) { - error("Could not update layout in attempt $attempt->id!"); - } - - // set originalquestion in states - set_field('quiz_states', 'originalquestion', $oldquestionid, 'attempt', $attempt->uniqueid, 'question', $question->id, 'originalquestion', '0'); - - // replace question id in states - set_field('quiz_states', 'question', $question->id, 'attempt', $attempt->uniqueid, 'question', $oldquestionid); - - // replace question id in sessions - set_field('question_sessions', 'questionid', $question->id, 'attemptid', $attempt->uniqueid, 'questionid', $oldquestionid); - - } - - // Now do anything question-type specific that is required to replace the question - // For example questions that use the quiz_answers table to hold part of their question will - // have to recode the answer ids in the states - $QUIZ_QTYPES[$question->qtype]->change_states_question($oldquestionid, $question, $attempts); - } - } - } - } else { - // use the old code which simply overwrites old versions - // it is also used for creating new questions - $question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course); - $replaceinquiz = 'all'; - } - - if (empty($question->errors) && $QUIZ_QTYPES[$qtype]->finished_edit_wizard($form)) { - // DISABLED AUTOMATIC REGRADING - // Automagically regrade all attempts (and states) in the affected quizzes - //if (!empty($replaceinquiz)) { - // $QUIZ_QTYPES[$question->qtype]->get_question_options($question); - // quiz_regrade_question_in_quizzes($question, $replaceinquiz); - //} - redirect("edit.php"); - } - } - - $grades = array(1,0.9,0.8,0.75,0.70,0.66666,0.60,0.50,0.40,0.33333,0.30,0.25,0.20,0.16666,0.142857,0.125,0.11111,0.10,0.05,0); - foreach ($grades as $grade) { - $percentage = 100 * $grade; - $neggrade = -$grade; - $gradeoptions["$grade"] = "$percentage %"; - $gradeoptionsfull["$grade"] = "$percentage %"; - $gradeoptionsfull["$neggrade"] = -$percentage." %"; - } - $gradeoptionsfull["0"] = $gradeoptions["0"] = get_string("none"); - - arsort($gradeoptions, SORT_NUMERIC); - arsort($gradeoptionsfull, SORT_NUMERIC); - - if (!$categories = quiz_get_category_menu($course->id, false)) { - error("No categories!"); - } - - - make_upload_directory("$course->id"); // Just in case - $coursefiles = get_directory_list("$CFG->dataroot/$course->id", $CFG->moddata); - foreach ($coursefiles as $filename) { - if (mimeinfo("icon", $filename) == "image.gif") { - $images["$filename"] = $filename; - } - } - - // Print the question editing form - - if (empty($question->id)) { - $question->id = ""; - } - if (empty($question->name)) { - $question->name = ""; - } - if (empty($question->questiontext)) { - $question->questiontext = ""; - } - if (empty($question->image)) { - $question->image = ""; - } - if (!isset($question->penalty)) { - $question->penalty = 0.1; - } - if (!isset($question->defaultgrade)) { - $question->defaultgrade = 1; - } - - // Set up some Richtext editing if necessary - if ($usehtmleditor = can_use_richtext_editor()) { - $defaultformat = FORMAT_HTML; - } else { - $defaultformat = FORMAT_MOODLE; - } - - if (isset($question->errors)) { - $err = $question->errors; - } - - echo '
'; - print_simple_box_start('center'); - require_once('questiontypes/'.$QUIZ_QTYPES[$qtype]->name().'/editquestion.php'); - print_simple_box_end(); - - if ($usehtmleditor) { - use_html_editor('questiontext'); - } - - print_footer($course); - -?> diff --git a/mod/quiz/questionlib.php b/mod/quiz/questionlib.php deleted file mode 100644 index 4b4ed3d071f..00000000000 --- a/mod/quiz/questionlib.php +++ /dev/null @@ -1,1244 +0,0 @@ -dirroot.'/mod/quiz/constants.php'); - -/// QUIZ_QTYPES INITIATION ////////////////// - -/** -* Array holding question type objects -*/ -$QUIZ_QTYPES= array(); - -require_once("$CFG->dirroot/mod/quiz/questiontypes/questiontype.php"); -quiz_load_questiontypes(); - -/** -* Loads the questiontype.php file for each question type -* -* These files in turn instantiate the corresponding question type class -* and adds it to the $QUIZ_QTYPES array -*/ -function quiz_load_questiontypes() { - global $QUIZ_QTYPES; - global $CFG; - - $qtypenames= get_list_of_plugins('mod/quiz/questiontypes'); - foreach($qtypenames as $qtypename) { - // Instanciates all plug-in question types - $qtypefilepath= "$CFG->dirroot/mod/quiz/questiontypes/$qtypename/questiontype.php"; - - // echo "Loading $qtypename
"; // Uncomment for debugging - if (is_readable($qtypefilepath)) { - require_once($qtypefilepath); - } - } -} - -/// OTHER CLASSES ///////////////////////////////////////////////////////// - -/** -* This holds the options that are determined by the course module -*/ -class cmoptions { - /** - * Whether a new attempt should be based on the previous one. If true - * then a new attempt will start in a state where all responses are set - * to the last responses from the previous attempt. - */ - var $attemptonlast = false; - - /** - * Various option flags. The flags are accessed via bitwise operations - * using the constants defined in the CONSTANTS section above. - */ - var $optionflags = QUIZ_ADAPTIVE; - - /** - * Determines whether in the calculation of the score for a question - * penalties for earlier wrong responses within the same attempt will - * be subtracted. - */ - var $penaltyscheme = true; - - /** - * The maximum time the user is allowed to answer the questions withing - * an attempt. This is measured in minutes so needs to be multiplied by - * 60 before compared to timestamps. If set to 0 no timelimit will be applied - */ - var $timelimit = 0; - - /** - * Timestamp for the closing time. Responses submitted after this time will - * be saved but no credit will be given for them. - */ - var $timeclose = 9999999999; - - /** - * The id of the course from withing which the question is currently being used - */ - var $course = SITEID; - - /** - * Whether the answers in a multiple choice question should be randomly - * shuffled when a new attempt is started. - */ - var $shuffleanswers = false; - - /** - * The number of decimals to be shown when scores are printed - */ - var $decimalpoints = 2; - - /** - * Determines when a student is allowed to review. The information is read - * out from the bits with the help of the constants defined earlier - * We initialise this to allow the student to see everything (all bits set) - */ - var $review = 16777215; -} - - - -/// FUNCTIONS ////////////////////////////////////////////////////// - - -/** -* Deletes question and all associated data from the database -* -* @param object $question The question being deleted -*/ -function quiz_delete_question($question) { - global $QUIZ_QTYPES; - $QUIZ_QTYPES[$question->qtype]->delete_question($question); - delete_records("quiz_answers", "question", $question->id); - delete_records("quiz_states", "question", $question->id); - delete_records("question_sessions", "questionid", $question->id); - if ($newversions = get_records('quiz_question_versions', 'oldquestion', $question->id)) { - foreach ($newversions as $newversion) { - $newquestion = get_record('quiz_questions', 'id', $newversion->newquestion); - quiz_delete_question($newquestion); - } - delete_records("quiz_question_versions", "oldquestion", $question->id); - } - delete_records("quiz_question_versions", "newquestion", $question->id); - if ($children = get_records('quiz_questions', 'parent', $question->id)) { - foreach ($children as $child) { - quiz_delete_question($child); - } - } - return true; -} - -/** -* Updates the question objects with question type specific -* information by calling {@link get_question_options()} -* -* Can be called either with an array of question objects or with a single -* question object. -* @return bool Indicates success or failure. -* @param mixed $questions Either an array of question objects to be updated -* or just a single question object -*/ -function quiz_get_question_options(&$questions) { - global $QUIZ_QTYPES; - - if (is_array($questions)) { // deal with an array of questions - // get the keys of the input array - $keys = array_keys($questions); - // update each question object - foreach ($keys as $i) { - // set name prefix - $questions[$i]->name_prefix = quiz_make_name_prefix($i); - - if (!$QUIZ_QTYPES[$questions[$i]->qtype]->get_question_options($questions[$i])) - return false; - } - return true; - } else { // deal with single question - $questions->name_prefix = quiz_make_name_prefix($questions->id); - return $QUIZ_QTYPES[$questions->qtype]->get_question_options($questions); - } -} - -/** -* Loads the most recent state of each question session from the database -* or create new one. -* -* For each question the most recent session state for the current attempt -* is loaded from the quiz_states table and the question type specific data and -* responses are added by calling {@link quiz_restore_state()} which in turn -* calls {@link restore_session_and_responses()} for each question. -* If no states exist for the question instance an empty state object is -* created representing the start of a session and empty question -* type specific information and responses are created by calling -* {@link create_session_and_responses()}. -* @todo Allow new attempt to be based on last attempt -* -* @return array An array of state objects representing the most recent -* states of the question sessions. -* @param array $questions The questions for which sessions are to be restored or -* created. -* @param object $cmoptions -* @param object $attempt The attempt for which the question sessions are -* to be restored or created. -*/ -function quiz_get_states(&$questions, $cmoptions, $attempt) { - global $CFG, $QUIZ_QTYPES; - - // get the question ids - $ids = array_keys($questions); - $questionlist = implode(',', $ids); - - // The question field must be listed first so that it is used as the - // array index in the array returned by get_records_sql - $statefields = 'n.questionid as question, s.*, n.sumpenalty'; - // Load the newest states for the questions - $sql = "SELECT $statefields". - " FROM {$CFG->prefix}quiz_states s,". - " {$CFG->prefix}question_sessions n". - " WHERE s.id = n.newest". - " AND n.attemptid = '$attempt->uniqueid'". - " AND n.questionid IN ($questionlist)"; - $states = get_records_sql($sql); - - // Load the newest graded states for the questions - $sql = "SELECT $statefields". - " FROM {$CFG->prefix}quiz_states s,". - " {$CFG->prefix}question_sessions n". - " WHERE s.id = n.newgraded". - " AND n.attemptid = '$attempt->uniqueid'". - " AND n.questionid IN ($questionlist)"; - $gradedstates = get_records_sql($sql); - - // loop through all questions and set the last_graded states - foreach ($ids as $i) { - if (isset($states[$i])) { - quiz_restore_state($questions[$i], $states[$i]); - if (isset($gradedstates[$i])) { - quiz_restore_state($questions[$i], $gradedstates[$i]); - $states[$i]->last_graded = $gradedstates[$i]; - } else { - $states[$i]->last_graded = clone($states[$i]); - $states[$i]->last_graded->responses = array('' => ''); - } - } else { - // Create a new state object - if ($cmoptions->attemptonlast and $attempt->attempt > 1 and !$attempt->preview) { - // build on states from last attempt - if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) { - error('Could not find previous attempt to build on'); - } - // Load the last graded state for the question - $sql = "SELECT $statefields". - " FROM {$CFG->prefix}quiz_states s,". - " {$CFG->prefix}question_sessions n". - " WHERE s.id = n.newgraded". - " AND n.attemptid = '$lastattemptid'". - " AND n.questionid = '$i'"; - if (!$states[$i] = get_record_sql($sql)) { - error('Could not find state for previous attempt to build on'); - } - quiz_restore_state($questions[$i], $states[$i]); - $states[$i]->attempt = $attempt->uniqueid; - $states[$i]->question = (int) $i; - $states[$i]->seq_number = 0; - $states[$i]->timestamp = $attempt->timestart; - $states[$i]->event = ($attempt->timefinish) ? QUIZ_EVENTCLOSE : QUIZ_EVENTOPEN; - $states[$i]->grade = 0; - $states[$i]->raw_grade = 0; - $states[$i]->penalty = 0; - $states[$i]->sumpenalty = 0; - $states[$i]->changed = true; - $states[$i]->last_graded = clone($states[$i]); - $states[$i]->last_graded->responses = array('' => ''); - - } else { - // create a new empty state - $states[$i] = new object; - $states[$i]->attempt = $attempt->uniqueid; - $states[$i]->question = (int) $i; - $states[$i]->seq_number = 0; - $states[$i]->timestamp = $attempt->timestart; - $states[$i]->event = ($attempt->timefinish) ? QUIZ_EVENTCLOSE : QUIZ_EVENTOPEN; - $states[$i]->grade = 0; - $states[$i]->raw_grade = 0; - $states[$i]->penalty = 0; - $states[$i]->sumpenalty = 0; - $states[$i]->responses = array('' => ''); - // Prevent further changes to the session from incrementing the - // sequence number - $states[$i]->changed = true; - - // Create the empty question type specific information - if (!$QUIZ_QTYPES[$questions[$i]->qtype] - ->create_session_and_responses($questions[$i], $states[$i], $cmoptions, $attempt)) { - return false; - } - $states[$i]->last_graded = clone($states[$i]); - } - } - } - return $states; -} - - -/** -* Creates the run-time fields for the states -* -* Extends the state objects for a question by calling -* {@link restore_session_and_responses()} -* @return boolean Represents success or failure -* @param object $question The question for which the state is needed -* @param object $state The state as loaded from the database -*/ -function quiz_restore_state(&$question, &$state) { - global $QUIZ_QTYPES; - - // initialise response to the value in the answer field - $state->responses = array('' => $state->answer); - unset($state->answer); - - // Set the changed field to false; any code which changes the - // question session must set this to true and must increment - // ->seq_number. The quiz_save_question_session - // function will save the new state object to the database if the field is - // set to true. - $state->changed = false; - - // Load the question type specific data - return $QUIZ_QTYPES[$question->qtype] - ->restore_session_and_responses($question, $state); - -} - -/** -* Saves the current state of the question session to the database -* -* The state object representing the current state of the session for the -* question is saved to the quiz_states table with ->responses[''] saved -* to the answer field of the database table. The information in the -* question_sessions table is updated. -* The question type specific data is then saved. -* @return boolean Indicates success or failure. -* @param object $question The question for which session is to be saved. -* @param object $state The state information to be saved. In particular the -* most recent responses are in ->responses. The object -* is updated to hold the new ->id. -*/ -function quiz_save_question_session(&$question, &$state) { - global $QUIZ_QTYPES; - // Check if the state has changed - if (!$state->changed && isset($state->id)) { - return true; - } - // Set the legacy answer field - $state->answer = isset($state->responses['']) ? $state->responses[''] : ''; - - // Save the state - if (isset($state->update)) { // this ->update field is only used by the - // regrading function to force the old state record to be overwritten - update_record('quiz_states', $state); - } else { - if (!$state->id = insert_record('quiz_states', $state)) { - unset($state->id); - unset($state->answer); - return false; - } - - // this is the most recent state - if (!record_exists('question_sessions', 'attemptid', - $state->attempt, 'questionid', $question->id)) { - $new->attemptid = $state->attempt; - $new->questionid = $question->id; - $new->newest = $state->id; - $new->sumpenalty = $state->sumpenalty; - if (!insert_record('question_sessions', $new)) { - error('Could not insert entry in question_sessions'); - } - } else { - set_field('question_sessions', 'newest', $state->id, 'attemptid', - $state->attempt, 'questionid', $question->id); - } - if (quiz_state_is_graded($state)) { - // this is also the most recent graded state - if ($newest = get_record('question_sessions', 'attemptid', - $state->attempt, 'questionid', $question->id)) { - $newest->newgraded = $state->id; - $newest->sumpenalty = $state->sumpenalty; - update_record('question_sessions', $newest); - } - } - } - - unset($state->answer); - - // Save the question type specific state information and responses - if (!$QUIZ_QTYPES[$question->qtype]->save_session_and_responses( - $question, $state)) { - return false; - } - // Reset the changed flag - $state->changed = false; - return true; -} - -/** -* Determines whether a state has been graded by looking at the event field -* -* @return boolean true if the state has been graded -* @param object $state -*/ -function quiz_state_is_graded($state) { - return ($state->event == QUIZ_EVENTGRADE or $state->event == QUIZ_EVENTCLOSE); -} - -/** -* Updates a state object for the next new state to record the fact that the -* question session has changed -* -* If the question session is not already marked as having changed (via the -* ->changed field of the state object), then this is done, the sequence -* number in ->seq_number is incremented and the timestamp in ->timestamp is -* updated. This should be called before or after any code which changes the -* question session. -* @param object $state The state object representing the state of the session. -*/ -function quiz_mark_session_change(&$state) { - if (!$state->changed) { - $state->changed = true; - $state->seq_number++; - $state->timestamp = time(); - } -} - - -/** -* Extracts responses from submitted form -* -* TODO: Finish documenting this -* @return array array of action objects, indexed by question ids. -* @param array $questions an array containing at least all questions that are used on the form -* @param array $responses -* @param integer $defaultevent -*/ -function quiz_extract_responses($questions, $responses, $defaultevent) { - - $actions = array(); - foreach ($responses as $key => $response) { - // Get the question id from the response name - if (false !== ($quid = quiz_get_id_from_name_prefix($key))) { - // check if this is a valid id - if (!isset($questions[$quid])) { - error('Form contained question that is not in questionids'); - } - - // Remove the name prefix from the name - //decrypt trying - $key = substr($key, strlen($questions[$quid]->name_prefix)); - if (false === $key) { - $key = ''; - } - // Check for question validate and mark buttons & set events - if ($key === 'validate') { - $actions[$quid]->event = QUIZ_EVENTVALIDATE; - } else if ($key === 'mark') { - $actions[$quid]->event = QUIZ_EVENTGRADE; - } else { - $actions[$quid]->event = $defaultevent; - } - - // Update the state with the new response - $actions[$quid]->responses[$key] = $response; - } - } - return $actions; -} - - - -/** -* For a given question in an attempt we walk the complete history of states -* and recalculate the grades as we go along. -* -* This is used when a question is changed and old student -* responses need to be marked with the new version of a question. -* -* TODO: Finish documenting this -* @return boolean Indicates success/failure -* @param object $question A question object -* @param object $attempt The attempt, in which the question needs to be regraded. -* @param object $cmoptions -* @param boolean $verbose Optional. Whether to print progress information or not. -*/ -function quiz_regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose=false) { - - // load all states for this question in this attempt, ordered in sequence - if ($states = get_records_select('quiz_states', - "attempt = '{$attempt->uniqueid}' AND question = '{$question->id}'", 'seq_number ASC')) { - $states = array_values($states); - - // Subtract the grade for the latest state from $attempt->sumgrades to get the - // sumgrades for the attempt without this question. - $attempt->sumgrades -= $states[count($states)-1]->grade; - - // Initialise the replaystate - $state = clone($states[0]); - quiz_restore_state($question, $state); - $state->sumpenalty = 0.0; - $replaystate = clone($state); - $replaystate->last_graded = $state; - - $changed = 0; - for($j = 1; $j < count($states); $j++) { - quiz_restore_state($question, $states[$j]); - $action = new stdClass; - $action->responses = $states[$j]->responses; - $action->timestamp = $states[$j]->timestamp; - - // Close the last state of a finished attempt - if (((count($states) - 1) === $j) && ($attempt->timefinish > 0)) { - $action->event = QUIZ_EVENTCLOSE; - - // Grade instead of closing, quiz_process_responses will then - // work out whether to close it - } else if (QUIZ_EVENTCLOSE == $states[$j]->event) { - $action->event = QUIZ_EVENTGRADE; - - // By default take the event that was saved in the database - } else { - $action->event = $states[$j]->event; - } - // Reprocess (regrade) responses - if (!quiz_process_responses($question, $replaystate, $action, $cmoptions, - $attempt)) { - $verbose && notify("Couldn't regrade state #{$state->id}!"); - } - - // We need rounding here because grades in the DB get truncated - // e.g. 0.33333 != 0.3333333, but we want them to be equal here - if (round((float)$replaystate->grade, 5) != round((float)$states[$j]->grade, 5)) { - $changed++; - } - - $replaystate->id = $states[$j]->id; - $replaystate->update = true; - quiz_save_question_session($question, $replaystate); - } - if ($verbose) { - if ($changed) { - link_to_popup_window ('/mod/quiz/reviewquestion.php?attempt='.$attempt->id.'&question='.$question->id, - 'reviewquestion', ' #'.$attempt->id, 450, 550, get_string('reviewresponse', 'quiz')); - update_record('quiz_attempts', $attempt); - } else { - echo ' #'.$attempt->id; - } - echo "\n"; @flush(); @ob_flush(); - } - - return true; - } - return true; -} - -/** -* Processes an array of student responses, grading and saving them as appropriate -* -* @return boolean Indicates success/failure -* @param object $question Full question object, passed by reference -* @param object $state Full state object, passed by reference -* @param object $action object with the fields ->responses which -* is an array holding the student responses, -* ->action which specifies the action, e.g., QUIZ_EVENTGRADE, -* and ->timestamp which is a timestamp from when the responses -* were submitted by the student. -* @param object $cmoptions -* @param object $attempt The attempt is passed by reference so that -* during grading its ->sumgrades field can be updated -*/ -function quiz_process_responses(&$question, &$state, $action, $cmoptions, &$attempt) { - global $QUIZ_QTYPES; - - // if no responses are set initialise to empty response - if (!isset($action->responses)) { - $action->responses = array('' => ''); - } - - // make sure these are gone! - unset($action->responses['mark'], $action->responses['validate']); - - // Check the question session is still open - if (QUIZ_EVENTCLOSE == $state->event) { - return true; - } - // If $action->event is not set that implies saving - if (! isset($action->event)) { - $action->event = QUIZ_EVENTSAVE; - } - // Check if we are grading the question; compare against last graded - // responses, not last given responses in this case - if (quiz_isgradingevent($action->event)) { - $state->responses = $state->last_graded->responses; - } - // Check for unchanged responses (exactly unchanged, not equivalent). - // We also have to catch questions that the student has not yet attempted - $sameresponses = (($state->responses == $action->responses) or - ($state->responses == array(''=>'') && array_keys(array_count_values($action->responses))===array(''))); - - if ($sameresponses and QUIZ_EVENTCLOSE != $action->event - and QUIZ_EVENTVALIDATE != $action->event) { - return true; - } - - // Roll back grading information to last graded state and set the new - // responses - $newstate = clone($state->last_graded); - $newstate->responses = $action->responses; - $newstate->seq_number = $state->seq_number + 1; - $newstate->changed = true; // will assure that it gets saved to the database - $newstate->last_graded = $state->last_graded; - $newstate->timestamp = $action->timestamp; - $state = $newstate; - - // Set the event to the action we will perform. The question type specific - // grading code may override this by setting it to QUIZ_EVENTCLOSE if the - // attempt at the question causes the session to close - $state->event = $action->event; - - if (!quiz_isgradingevent($action->event)) { - // Grade the response but don't update the overall grade - $QUIZ_QTYPES[$question->qtype]->grade_responses( - $question, $state, $cmoptions); - // Force the event to save or validate (even if the grading caused the - // state to close) - $state->event = $action->event; - - } else if (QUIZ_EVENTGRADE == $action->event) { - - // Work out if the current responses (or equivalent responses) were - // already given in - // a. the last graded attempt - // b. any other graded attempt - if($QUIZ_QTYPES[$question->qtype]->compare_responses( - $question, $state, $state->last_graded)) { - $state->event = QUIZ_EVENTDUPLICATEGRADE; - } else { - if ($cmoptions->optionflags & QUIZ_IGNORE_DUPRESP) { - /* Walk back through the previous graded states looking for - one where the responses are equivalent to the current - responses. If such a state is found, set the current grading - details to those of that state and set the event to - QUIZ_EVENTDUPLICATEGRADE */ - quiz_search_for_duplicate_responses($question, $state); - } - // If we did not find a duplicate, perform grading - if (QUIZ_EVENTDUPLICATEGRADE != $state->event) { - // Decrease sumgrades by previous grade and then later add new grade - $attempt->sumgrades -= (float)$state->last_graded->grade; - - $QUIZ_QTYPES[$question->qtype]->grade_responses( - $question, $state, $cmoptions); - // Calculate overall grade using correct penalty method - quiz_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); - // Update the last graded state (don't simplify!) - unset($state->last_graded); - $state->last_graded = clone($state); - unset($state->last_graded->changed); - - $attempt->sumgrades += (float)$state->last_graded->grade; - } - } - } else if (QUIZ_EVENTCLOSE == $action->event) { - // decrease sumgrades by previous grade and then later add new grade - $attempt->sumgrades -= (float)$state->last_graded->grade; - - // Only mark if they haven't been marked already - if (!$sameresponses) { - $QUIZ_QTYPES[$question->qtype]->grade_responses( - $question, $state, $cmoptions); - // Calculate overall grade using correct penalty method - quiz_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); - } - // Force the state to close (as the attempt is closing) - $state->event = QUIZ_EVENTCLOSE; - - // Update the last graded state (don't simplify!) - unset($state->last_graded); - $state->last_graded = clone($state); - unset($state->last_graded->changed); - - $attempt->sumgrades += (float)$state->last_graded->grade; - } - $attempt->timemodified = $action->timestamp; - - return true; -} - -/** -* Determine if event requires grading -*/ -function quiz_isgradingevent($event) { - return (QUIZ_EVENTGRADE == $event || QUIZ_EVENTCLOSE == $event); -} - -/** -* Compare current responses to all previous graded responses -* -* This is used by {@link quiz_process_responses()} to determine whether -* to ignore the marking request for the current response. However this -* check against all previous graded responses is only performed if -* the QUIZ_IGNORE_DUPRESP bit in $cmoptions->optionflags is set -* @return boolean Indicates if a state with duplicate responses was -* found. -* @param object $question -* @param object $state -*/ -function quiz_search_for_duplicate_responses(&$question, &$state) { - // get all previously graded question states - global $QUIZ_QTYPES; - if (!$oldstates = get_records('quiz_question_states', "event = '" . - QUIZ_EVENTGRADE . "' AND " . "question = '" . $question->id . - "'", 'seq_number DESC')) { - return false; - } - foreach ($oldstates as $oldstate) { - if ($QUIZ_QTYPES[$question->qtype]->restore_session_and_responses( - $question, $oldstate)) { - if(!$QUIZ_QTYPES[$question->qtype]->compare_responses( - $question, $state, $oldstate)) { - $state->event = QUIZ_EVENTDUPLICATEGRADE; - break; - } - } - } - return (QUIZ_EVENTDUPLICATEGRADE == $state->event); -} - -/** -* Applies the penalty from the previous graded responses to the raw grade -* for the current responses -* -* The grade for the question in the current state is computed by subtracting the -* penalty accumulated over the previous graded responses at the question from the -* raw grade. If the timestamp is more than 1 minute beyond the end of the attempt -* the grade is set to zero. The ->grade field of the state object is modified to -* reflect the new grade but is never allowed to decrease. -* @param object $question The question for which the penalty is to be applied. -* @param object $state The state for which the grade is to be set from the -* raw grade and the cumulative penalty from the last -* graded state. The ->grade field is updated by applying -* the penalty scheme determined in $cmoptions to the ->raw_grade and -* ->last_graded->penalty fields. -* @param object $cmoptions The options set by the course module. -* The ->penaltyscheme field determines whether penalties -* for incorrect earlier responses are subtracted. -*/ -function quiz_apply_penalty_and_timelimit(&$question, &$state, $attempt, $cmoptions) { - // deal with penaly - if ($cmoptions->penaltyscheme) { - $state->grade = $state->raw_grade - $state->sumpenalty; - $state->sumpenalty += (float) $state->penalty; - } else { - $state->grade = $state->raw_grade; - } - - // deal with timelimit - if ($cmoptions->timelimit) { - // We allow for 5% uncertainty in the following test - if (($state->timestamp - $attempt->timestart) > ($cmoptions->timelimit * 63)) { - $state->grade = 0; - } - } - - // deal with closing time - if ($cmoptions->timeclose and $state->timestamp > ($cmoptions->timeclose + 60) // allowing 1 minute lateness - and !$attempt->preview) { // ignore closing time for previews - $state->grade = 0; - } - - // Ensure that the grade does not go down - $state->grade = max($state->grade, $state->last_graded->grade); -} - - -function quiz_print_comment($text) { - echo " ".format_text($text, true, false).""; -} - -function quiz_print_correctanswer($text) { - echo "

$text

"; -} - -/** -* Print the icon for the question type -* -* @param object $question The question object for which the icon is required -* @param boolean $editlink If true then the icon is a link to the question -* edit page. -* @param boolean $return If true the functions returns the link as a string -*/ -function quiz_print_question_icon($question, $editlink=true, $return = false) { -// returns a question icon - - global $QUIZ_QUESTION_TYPE; - global $QUIZ_QTYPES; - - $html = ''.
-            get_string($QUIZ_QTYPES[$question->qtype]->name(), 'quiz').''; - - if ($editlink) { - $html = "id\" title=\"" - .$QUIZ_QTYPES[$question->qtype]->name()."\">". - $html."\n"; - } - if ($return) { - return $html; - } else { - echo $html; - } -} - - -/** -* Returns a html link to the question image if there is one -* -* @return string The html image tag or the empy string if there is no image. -* @param object $question The question object -*/ -function quiz_get_image($question, $courseid) { - - global $CFG; - $img = ''; - - if ($question->image) { - - if (substr(strtolower($question->image), 0, 7) == 'http://') { - $img .= $question->image; - - } else if ($CFG->slasharguments) { // Use this method if possible for better caching - $img .= "$CFG->wwwroot/file.php/$courseid/$question->image"; - - } else { - $img .= "$CFG->wwwroot/file.php?file=$courseid/$question->image"; - } - } - return $img; -} - -/** -* Print the question image if there is one -* -* @param object $question The question object -*/ -function quiz_print_possible_question_image($question, $courseid) { - - global $CFG; - - if ($question->image) { - echo 'wwwroot/file.php/$courseid/$question->image"; - - } else { - echo "$CFG->wwwroot/file.php?file=$courseid/$question->image"; - } - echo '" alt="" />'; - - } -} - -/** -* Construct name prefixes for question form element names -* -* Construct the name prefix that should be used for example in the -* names of form elements created by questions. -* This is called by {@link quiz_get_question_options()} -* to set $question->name_prefix. -* This name prefix includes the question id which can be -* extracted from it with {@link quiz_get_id_from_name_prefix()}. -* -* @return string -* @param integer $id The question id -*/ -function quiz_make_name_prefix($id) { - return 'resp' . $id . '_'; -} - -/** -* Extract question id from the prefix of form element names -* -* @return integer The question id -* @param string $name The name that contains a prefix that was -* constructed with {@link quiz_make_name_prefix()} -*/ -function quiz_get_id_from_name_prefix($name) { - if (!preg_match('/^resp([0-9]+)_/', $name, $matches)) - return false; - return (integer) $matches[1]; -} - -function quiz_new_attempt_uniqueid() { - global $CFG; - set_config('attemptuniqueid', $CFG->attemptuniqueid + 1); - return $CFG->attemptuniqueid; -} - -/** -* Determine render options -*/ -function quiz_get_renderoptions($cmoptions, $state) { - // Show the question in readonly (review) mode if the question is in - // the closed state - $options->readonly = QUIZ_EVENTCLOSE === $state->event; - - // Show feedback once the question has been graded (if allowed by the quiz) - $options->feedback = ($state->event == QUIZ_EVENTGRADE) && ($cmoptions->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY); - - // Show validation only after a validation event - $options->validation = QUIZ_EVENTVALIDATE === $state->event; - - // Show correct responses in readonly mode if the quiz allows it - $options->correct_responses = $options->readonly && ($cmoptions->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY); - - // Always show responses and scores - $options->responses = true; - $options->scores = true; - - return $options; -} - - -/** -* Determine review options -*/ -function quiz_get_reviewoptions($cmoptions, $attempt, $isteacher=false) { - $options->readonly = true; - if ($isteacher and !$attempt->preview) { - // The teacher should be shown everything except during preview when the teachers - // wants to see just what the students see - $options->responses = true; - $options->scores = true; - $options->feedback = true; - $options->correct_responses = true; - $options->solutions = false; - return $options; - } - if ((time() - $attempt->timefinish) < 120) { - $options->responses = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_RESPONSES) ? 1 : 0; - $options->scores = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SCORES) ? 1 : 0; - $options->feedback = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_FEEDBACK) ? 1 : 0; - $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_ANSWERS) ? 1 : 0; - $options->solutions = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0; - } else if (!$cmoptions->timeclose or time() < $cmoptions->timeclose) { - $options->responses = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_RESPONSES) ? 1 : 0; - $options->scores = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SCORES) ? 1 : 0; - $options->feedback = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_FEEDBACK) ? 1 : 0; - $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_ANSWERS) ? 1 : 0; - $options->solutions = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0; - } else { - $options->responses = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES) ? 1 : 0; - $options->scores = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SCORES) ? 1 : 0; - $options->feedback = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_FEEDBACK) ? 1 : 0; - $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_ANSWERS) ? 1 : 0; - $options->solutions = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0; - } - return $options; -} - -/// FUNCTIONS THAT ARE USED BY SOME QUESTIONTYPES /////////////////// - -function quiz_extract_correctanswers($answers, $nameprefix) { -/// Convenience function that is used by some single-response -/// question-types for determining correct answers. - - $bestanswerfraction = 0.0; - $correctanswers = array(); - foreach ($answers as $answer) { - if ($answer->fraction > $bestanswerfraction) { - $correctanswers = array($nameprefix.$answer->id => $answer); - $bestanswerfraction = $answer->fraction; - } else if ($answer->fraction == $bestanswerfraction) { - $correctanswers[$nameprefix.$answer->id] = $answer; - } - } - return $correctanswers; -} - -/// FUNCTIONS THAT SIMPLY WRAP QUESTIONTYPE METHODS ////////////////////////////////// - -/** -* Prints a question -* -* Simply calls the question type specific print_question() method. -*/ -function quiz_print_quiz_question(&$question, &$state, $number, $cmoptions, $options=null) { - global $QUIZ_QTYPES; - - $QUIZ_QTYPES[$question->qtype]->print_question($question, $state, $number, - $cmoptions, $options); -} - -/** -* Gets all teacher stored answers for a given question -* -* Simply calls the question type specific get_all_responses() method. -*/ -// ULPGC ecastro -function quiz_get_question_responses($question, $state) { - global $QUIZ_QTYPES; - $r = $QUIZ_QTYPES[$question->qtype]->get_all_responses($question, $state); - return $r; -} - - -/** -* Gets the response given by the user in a particular attempt -* -* Simply calls the question type specific get_actual_response() method. -*/ -// ULPGC ecastro -function quiz_get_question_actual_response($question, $state) { - global $QUIZ_QTYPES; - - $r = $QUIZ_QTYPES[$question->qtype]->get_actual_response($question, $state); - return $r; -} - -/** -* Gets the response given by the user in a particular attempt -* -* Simply calls the question type specific get_actual_response() method. -*/ -// ULPGc ecastro -function quiz_get_question_fraction_grade($question, $state) { - global $QUIZ_QTYPES; - - $r = $QUIZ_QTYPES[$question->qtype]->get_fractional_grade($question, $state); - return $r; -} - - -/// CATEGORY FUNCTIONS ///////////////////////////////////////////////////////////////// - -/** -* Gets the default category in a course -* -* It returns the first category with no parent category. If no categories -* exist yet then one is created. -* @return object The default category -* @param integer $courseid The id of the course whose default category is wanted -*/ -function quiz_get_default_category($courseid) { -/// Returns the current category - - if ($categories = get_records_select("quiz_categories", "course = '$courseid' AND parent = '0'", "id")) { - foreach ($categories as $category) { - return $category; // Return the first one (lowest id) - } - } - - // Otherwise, we need to make one - $category->name = get_string("default", "quiz"); - $category->info = get_string("defaultinfo", "quiz"); - $category->course = $courseid; - $category->parent = 0; - $category->sortorder = QUIZ_CATEGORIES_SORTORDER; - $category->publish = 0; - $category->stamp = make_unique_id_code(); - - if (!$category->id = insert_record("quiz_categories", $category)) { - notify("Error creating a default category!"); - return false; - } - return $category; -} - -function quiz_get_category_menu($courseid, $published=false) { -/// Returns the list of categories - $publish = ""; - if ($published) { - $publish = "OR publish = '1'"; - } - - if (!isadmin()) { - $categories = get_records_select("quiz_categories", "course = '$courseid' $publish", 'parent, sortorder, name ASC'); - } else { - $categories = get_records_select("quiz_categories", '', 'parent, sortorder, name ASC'); - } - if (!$categories) { - return false; - } - $categories = add_indented_names($categories); - - foreach ($categories as $category) { - if ($catcourse = get_record("course", "id", $category->course)) { - if ($category->publish && ($category->course != $courseid)) { - $category->indentedname .= " ($catcourse->shortname)"; - } - $catmenu[$category->id] = $category->indentedname; - } - } - return $catmenu; -} - -function sort_categories_by_tree(&$categories, $id = 0, $level = 1) { -// returns the categories with their names ordered following parent-child relationships -// finally it tries to return pending categories (those being orphaned, whose parent is -// incorrect) to avoid missing any category from original array. - $children = array(); - $keys = array_keys($categories); - - foreach ($keys as $key) { - if (!isset($categories[$key]->processed) && $categories[$key]->parent == $id) { - $children[$key] = $categories[$key]; - $categories[$key]->processed = true; - $children = $children + sort_categories_by_tree($categories, $children[$key]->id, $level+1); - } - } - //If level = 1, we have finished, try to look for non processed categories (bad parent) and sort them too - if ($level == 1) { - foreach ($keys as $key) { - //If not processed and it's a good candidate to start (because its parent doesn't exist in the course) - if (!isset($categories[$key]->processed) && !record_exists('quiz_categories', 'course', $categories[$key]->course, 'id', $categories[$key]->parent)) { - $children[$key] = $categories[$key]; - $categories[$key]->processed = true; - $children = $children + sort_categories_by_tree($categories, $children[$key]->id, $level+1); - } - } - } - return $children; -} - -function add_indented_names(&$categories, $id = 0, $indent = 0) { -// returns the categories with their names indented to show parent-child relationships - $fillstr = '   '; - $fill = str_repeat($fillstr, $indent); - $children = array(); - $keys = array_keys($categories); - - foreach ($keys as $key) { - if (!isset($categories[$key]->processed) && $categories[$key]->parent == $id) { - $children[$key] = $categories[$key]; - $children[$key]->indentedname = $fill . $children[$key]->name; - $categories[$key]->processed = true; - $children = $children + add_indented_names($categories, $children[$key]->id, $indent + 1); - } - } - return $children; -} - -/** -* Displays a select menu of categories with appended course names -* -* Optionaly non editable categories may be excluded. -* @author Howard Miller June '04 -*/ -function quiz_category_select_menu($courseid,$published=false,$only_editable=false,$selected="") { - - // get sql fragment for published - $publishsql=""; - if ($published) { - $publishsql = "or publish=1"; - } - - $categories = get_records_select("quiz_categories","course=$courseid $publishsql", 'parent, sortorder, name ASC'); - - $categories = add_indented_names($categories); - - echo "\n"; -} - -function quiz_get_category_coursename($category, $courseid = 0) { -/// if the category is not from this course and is published , adds on the course -/// name - $cname = (isset($category->indentedname)) ? $category->indentedname : $category->name; - if ($category->course != $courseid && $category->publish) { - if ($catcourse=get_record("course","id",$category->course)) { - $cname .= " ($catcourse->shortname) "; - } - } - return $cname; -} - - -/** -* Returns a comma separated list of ids of the category and all subcategories -*/ -function quiz_categorylist($categoryid) { - // returns a comma separated list of ids of the category and all subcategories - $categorylist = $categoryid; - if ($subcategories = get_records('quiz_categories', 'parent', $categoryid, 'sortorder ASC', 'id, id')) { - foreach ($subcategories as $subcategory) { - $categorylist .= ','. quiz_categorylist($subcategory->id); - } - } - return $categorylist; -} - - -/** -* Function to read all questions for category into big array -* -* @param int $category category number -* @param bool @noparent if true only questions with NO parent will be selected -* @author added by Howard Miller June 2004 -*/ -function get_questions_category( $category, $noparent=false ) { - - global $QUIZ_QTYPES; - - // questions will be added to an array - $qresults = array(); - - // build sql bit for $noparent - $npsql = ''; - if ($noparent) { - $npsql = " and parent='0' "; - } - - // get the list of questions for the category - if ($questions = get_records_select("quiz_questions","category={$category->id} $npsql", "qtype, name ASC")) { - - // iterate through questions, getting stuff we need - foreach($questions as $question) { - $questiontype = $QUIZ_QTYPES[$question->qtype]; - $questiontype->get_question_options( $question ); - $qresults[] = $question; - } - } - - return $qresults; -} - -?> diff --git a/mod/quiz/report/analysis/report.php b/mod/quiz/report/analysis/report.php index 8fd38e1f8e2..5446f6f94e3 100644 --- a/mod/quiz/report/analysis/report.php +++ b/mod/quiz/report/analysis/report.php @@ -89,8 +89,8 @@ class quiz_report extends quiz_default_report { } $sql .= ' WHERE qa.quiz = '.$quiz->id. // ULPGC ecastro ' AND ( qa.sumgrades >= '.$scorelimit.' ) '; - // ^^^^^^ es posible seleccionar aquí TODOS los quizzes, como quiere Jussi, - // pero habría que llevar la cuenta ed cada quiz para restaura las preguntas (quizquestions, states) + // ^^^^^^ es posible seleccionar aqu� TODOS los quizzes, como quiere Jussi, + // pero habr�a que llevar la cuenta ed cada quiz para restaura las preguntas (quizquestions, states) /// Fetch the attempts $attempts = get_records_sql($sql); @@ -309,7 +309,7 @@ class quiz_report extends quiz_default_report { $q = $questions[$qnum]; $qid = $q['id']; $question = get_record('quiz_questions', 'id', $qid); - $qnumber = " (".link_to_popup_window('/mod/quiz/question.php?id='.$qid,'editquestion', $qid, 450, 550, get_string('edit'), 'none', true ).") "; + $qnumber = " (".link_to_popup_window('/question/question.php?id='.$qid,'editquestion', $qid, 450, 550, get_string('edit'), 'none', true ).") "; $qname = '
'.format_text($question->name." : ", $question->questiontextformat, NULL, $quiz->course).'
'; $qicon = quiz_print_question_icon($question, false, true); $qreview = quiz_get_question_review($quiz, $question); diff --git a/mod/quiz/report/default.php b/mod/quiz/report/default.php index 5ddb69988fa..5acfd6dd96a 100644 --- a/mod/quiz/report/default.php +++ b/mod/quiz/report/default.php @@ -22,6 +22,7 @@ class quiz_default_report { } function print_header_and_tabs($cm, $course, $quiz, $reportmode="overview"){ + global $CFG; /// Define some strings $strquizzes = get_string("modulenameplural", "quiz"); $strquiz = get_string("modulename", "quiz"); diff --git a/mod/quiz/restorelib.php b/mod/quiz/restorelib.php index 51c4a9cac97..f3578476d41 100644 --- a/mod/quiz/restorelib.php +++ b/mod/quiz/restorelib.php @@ -1538,7 +1538,7 @@ global $CFG; - include($CFG->dirroot.'/mod/quiz/questionlib.php'); + include($CFG->dirroot.'/question/lib.php'); $status = true; diff --git a/mod/quiz/tabs.php b/mod/quiz/tabs.php index e27dd78dede..3240bc24017 100644 --- a/mod/quiz/tabs.php +++ b/mod/quiz/tabs.php @@ -28,12 +28,12 @@ $row = array(); $inactive = array(); - $row[] = new tabobject('info', "view.php?q=$quiz->id", get_string('info', 'quiz')); - $row[] = new tabobject('reports', "report.php?q=$quiz->id", get_string('reports', 'quiz')); - $row[] = new tabobject('preview', "attempt.php?q=$quiz->id", get_string('preview', 'quiz'), get_string('previewquiz', 'quiz', format_string($quiz->name))); + $row[] = new tabobject('info', "$CFG->wwwroot/mod/quiz/view.php?q=$quiz->id", get_string('info', 'quiz')); + $row[] = new tabobject('reports', "$CFG->wwwroot/mod/quiz/report.php?q=$quiz->id", get_string('reports', 'quiz')); + $row[] = new tabobject('preview', "$CFG->wwwroot/mod/quiz/attempt.php?q=$quiz->id", get_string('preview', 'quiz'), get_string('previewquiz', 'quiz', format_string($quiz->name))); if (isteacheredit($course->id)) { - $row[] = new tabobject('edit', "edit.php?quizid=$quiz->id", get_string('edit'), get_string('editquizquestions', 'quiz')); - $row[] = new tabobject('manualgrading', "grading.php?quizid=$quiz->id", get_string("manualgrading", "quiz")); + $row[] = new tabobject('edit', "$CFG->wwwroot/mod/quiz/edit.php?quizid=$quiz->id", get_string('edit'), get_string('editquizquestions', 'quiz')); + $row[] = new tabobject('manualgrading', "$CFG->wwwroot/mod/quiz/grading.php?quizid=$quiz->id", get_string("manualgrading", "quiz")); } $tabs[] = $row; @@ -52,7 +52,7 @@ $row = array(); $currenttab = ''; foreach ($reportlist as $report) { - $row[] = new tabobject($report, "report.php?q=$quiz->id&mode=$report", + $row[] = new tabobject($report, "$CFG->wwwroot/mod/quiz/report.php?q=$quiz->id&mode=$report", get_string("report$report", "quiz")); if ($report == $mode) { $currenttab = $report; @@ -67,10 +67,10 @@ $row = array(); $currenttab = $mode; - $row[] = new tabobject('editq', "edit.php?quizid=$quiz->id", get_string('questions', 'quiz'), get_string('editquizquestions', 'quiz')); - $row[] = new tabobject('categories', "category.php?id=$course->id", get_string('categories', 'quiz'), get_string('editqcats', 'quiz')); - $row[] = new tabobject('import', "import.php?course=$course->id", get_string('import', 'quiz'), get_string('importquestions', 'quiz')); - $row[] = new tabobject('export', "export.php?courseid=$course->id", get_string('export', 'quiz'), get_string('exportquestions', 'quiz')); + $row[] = new tabobject('editq', "$CFG->wwwroot/mod/quiz/edit.php?quizid=$quiz->id", get_string('questions', 'quiz'), get_string('editquizquestions', 'quiz')); + $row[] = new tabobject('categories', "$CFG->wwwroot/question/category.php?id=$course->id", get_string('categories', 'quiz'), get_string('editqcats', 'quiz')); + $row[] = new tabobject('import', "$CFG->wwwroot/mod/quiz/import.php?course=$course->id", get_string('import', 'quiz'), get_string('importquestions', 'quiz')); + $row[] = new tabobject('export', "$CFG->wwwroot/mod/quiz/export.php?courseid=$course->id", get_string('export', 'quiz'), get_string('exportquestions', 'quiz')); $row[] = new tabobject('update', "$CFG->wwwroot/course/mod.php?update=$cm->id&sesskey=$USER->sesskey", get_string('settings'), get_string('updatesettings')); $tabs[] = $row;