. /** * Strings for component 'tool_health', language 'en', branch 'MOODLE_22_STABLE' * * @package tool * @subpackage health * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ ob_start(); //for whitespace test require('../../../config.php'); $extraws = ob_get_clean(); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/health/locallib.php'); admin_externalpage_setup('toolhealth'); define('SEVERITY_NOTICE', 'notice'); define('SEVERITY_ANNOYANCE', 'annoyance'); define('SEVERITY_SIGNIFICANT', 'significant'); define('SEVERITY_CRITICAL', 'critical'); $solution = optional_param('solution', 0, PARAM_PLUGIN); $site = get_site(); echo $OUTPUT->header(); if(strpos($solution, 'problem_') === 0 && class_exists($solution)) { health_print_solution($solution); } else { health_find_problems(); } echo $OUTPUT->footer(); function health_find_problems() { global $OUTPUT; echo $OUTPUT->heading(get_string('pluginname', 'tool_health')); $issues = array( SEVERITY_CRITICAL => array(), SEVERITY_SIGNIFICANT => array(), SEVERITY_ANNOYANCE => array(), SEVERITY_NOTICE => array(), ); $problems = 0; for($i = 1; $i < 1000000; ++$i) { $classname = sprintf('problem_%06d', $i); if(!class_exists($classname)) { continue; } $problem = new $classname; if($problem->exists()) { $severity = $problem->severity(); $issues[$severity][$classname] = array( 'severity' => $severity, 'description' => $problem->description(), 'title' => $problem->title() ); ++$problems; } unset($problem); } if($problems == 0) { echo '
There are two ways you can solve this problem:
session.auto_start = 1and change it to
session.auto_start = 0and then restart your web server. Be warned that this, as any other PHP setting change, might affect other web applications running on the server.
php_value session.auto_start "0"
There are two ways you can solve this problem:
file_uploads = Offand change it to
file_uploads = Onand then restart your web server. Be warned that this, as any other PHP setting change, might affect other web applications running on the server.
php_value file_uploads "On"
IIS:
cgi.fix_pathinfo=1
to php.iniApache 1:
cgi.fix_pathinfo=1
to php.iniApache 2:
AcceptPathInfo on
to php.ini or .htaccesscgi.fix_pathinfo=1
to php.iniFor random questions, question.parent should equal question.id. ' . 'There are some questions in your database for which this is not true. ' . 'One way that this could have happened is for random questions restored from backup before ' . 'MDL-5482 was fixed.
'; } function solution() { global $CFG; return 'Upgrade to Moodle 1.9.1 or later, or manually execute the SQL
' . 'UPDATE ' . $CFG->prefix . 'question SET parent = id WHERE qtype = \'random\' and parent <> id;'; } } class problem_000013 extends problem_base { function title() { return 'Multi-answer questions data consistency'; } function exists() { global $DB; $positionexpr = $DB->sql_position($DB->sql_concat("','", "q.id", "','"), $DB->sql_concat("','", "qma.sequence", "','")); return $DB->record_exists_sql(" SELECT * FROM {question} q JOIN {question_multianswer} qma ON $positionexpr > 0 WHERE qma.question <> q.parent") || $DB->record_exists_sql(" SELECT * FROM {question} q JOIN {question} parent_q ON parent_q.id = q.parent WHERE q.category <> parent_q.category"); } function severity() { return SEVERITY_ANNOYANCE; } function description() { return '
For each sub-question whose id is listed in ' . 'question_multianswer.sequence, its question.parent field should equal ' . 'question_multianswer.question; and each sub-question should be in the same ' . 'category as its parent. There are questions in your database for ' . 'which this is not the case. One way that this could have happened is ' . 'for multi-answer questions restored from backup before ' . 'MDL-14750 was fixed.
'; } function solution() { return 'Upgrade to Moodle 1.9.1 or later, or manually execute the ' . 'code in question_multianswer_fix_subquestion_parents_and_categories in ' . '/question/type/multianswer/db/upgrade.php' . 'from the 1.9 stable branch.
'; } } class problem_000014 extends problem_base { function title() { return 'Only multianswer and random questions should be the parent of another question'; } function exists() { global $DB; return $DB->record_exists_sql(" SELECT * FROM {question} q JOIN {question} parent_q ON parent_q.id = q.parent WHERE parent_q.qtype NOT IN ('random', 'multianswer')"); } function severity() { return SEVERITY_ANNOYANCE; } function description() { return 'You have questions that violate this in your databse. ' . 'You will need to investigate to determine how this happened.
'; } function solution() { return 'It is impossible to give a solution without knowing more about ' . ' how the problem was caused. You may be able to get help from the ' . 'Quiz forum.
'; } } class problem_000015 extends problem_base { function title() { return 'Question categories should belong to a valid context'; } function exists() { global $DB; return $DB->record_exists_sql(" SELECT qc.*, (SELECT COUNT(1) FROM {question} q WHERE q.category = qc.id) AS numquestions FROM {question_categories} qc LEFT JOIN {context} con ON qc.contextid = con.id WHERE con.id IS NULL"); } function severity() { return SEVERITY_ANNOYANCE; } function description() { global $DB; $problemcategories = $DB->get_records_sql(" SELECT qc.id, qc.name, qc.contextid, (SELECT COUNT(1) FROM {question} q WHERE q.category = qc.id) AS numquestions FROM {question_categories} qc LEFT JOIN {context} con ON qc.contextid = con.id WHERE con.id IS NULL ORDER BY numquestions DESC, qc.name"); $table = 'Cat id | Category name | ' . "Context id | Num Questions |
---|---|---|---|
$cat->id | " . s($cat->name) . " | " . $cat->contextid ." | $cat->numquestions |
All question categories are linked to a context id, and, ' . 'the context they are linked to must exist. The following categories ' . 'belong to a non-existant category:
' . $table . 'Any of these ' . 'categories that contain no questions can just be deleted form the database. ' . 'Other categories will require more thought.
'; } function solution() { global $CFG; return 'You can delete the empty categories by executing the following SQL:
DELETE FROM ' . $CFG->prefix . 'question_categories WHERE NOT EXISTS (SELECT * FROM ' . $CFG->prefix . 'question q WHERE q.category = ' . $CFG->prefix . 'question_categories.id) AND NOT EXISTS (SELECT * FROM ' . $CFG->prefix . 'context con WHERE contextid = con.id)
Any remaining categories that contain questions will require more thought. ' . 'People in the Quiz forum may be able to help.
'; } } class problem_000016 extends problem_base { function title() { return 'Question categories should belong to the same context as their parent'; } function exists() { global $DB; return $DB->record_exists_sql(" SELECT parent_qc.id AS parent, child_qc.id AS child, child_qc.contextid FROM {question_categories} child_qc JOIN {question_categories} parent_qc ON child_qc.parent = parent_qc.id WHERE child_qc.contextid <> parent_qc.contextid"); } function severity() { return SEVERITY_ANNOYANCE; } function description() { global $DB; $problemcategories = $DB->get_records_sql(" SELECT parent_qc.id AS parentid, parent_qc.name AS parentname, parent_qc.contextid AS parentcon, child_qc.id AS childid, child_qc.name AS childname, child_qc.contextid AS childcon FROM {question_categories} child_qc JOIN {question_categories} parent_qc ON child_qc.parent = parent_qc.id WHERE child_qc.contextid <> parent_qc.contextid"); $table = 'Child category | Parent category | ||||
---|---|---|---|---|---|
Id | Name | Context id | ' . 'Id | Name | Context id | ' . "
$cat->childid | " . s($cat->childname) . " | $cat->childcon | $cat->parentid | " . s($cat->parentname) . " | $cat->parentcon |
When one question category is the parent of another, then they ' . 'should both belong to the same context. This is not true for the following categories:
' . $table; } function solution() { return 'An automated solution is difficult. It depends whether the ' . 'parent or child category is in the wrong pace.' . 'People in the Quiz forum may be able to help.
'; } } class problem_000017 extends problem_base { function title() { return 'Question categories tree structure'; } function find_problems() { global $DB; static $answer = null; if (is_null($answer)) { $categories = $DB->get_records('question_categories', array(), 'id'); // Look for missing parents. $missingparent = tool_health_category_find_missing_parents($categories); // Look for loops. $loops = tool_health_category_find_loops($categories); $answer = array($missingparent, $loops); } return $answer; } function exists() { list($missingparent, $loops) = $this->find_problems(); return !empty($missingparent) || !empty($loops); } function severity() { return SEVERITY_ANNOYANCE; } function description() { list($missingparent, $loops) = $this->find_problems(); $description = 'The question categories should be arranged into tree ' . ' structures by the question_categories.parent field. Sometimes ' . ' this tree structure gets messed up.
'; $description .= tool_health_category_list_missing_parents($missingparent); $description .= tool_health_category_list_loops($loops); return $description; } /** * Outputs resolutions to problems outlined in MDL-34684 with items having themselves as parent * * @link https://tracker.moodle.org/browse/MDL-34684 * @return string Formatted html to be output to the browser with instructions and sql statements to run */ public function solution() { global $CFG; list($missingparent, $loops) = $this->find_problems(); $solution = 'Consider executing the following SQL queries. These fix ' . 'the problem by moving some categories to the top level.
'; if (!empty($missingparent)) { $solution .= "UPDATE " . $CFG->prefix . "question_categories\n" . " SET parent = 0\n" . " WHERE id IN (" . implode(',', array_keys($missingparent)) . ");\n"; } if (!empty($loops)) { $solution .= "
UPDATE " . $CFG->prefix . "question_categories\n" . " SET parent = 0\n" . " WHERE id IN (" . implode(',', array_keys($loops)) . ");\n"; } return $solution; } } /** * Check course categories tree structure for problems. * * @copyright 2013 Marko Vidberg * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class problem_000018 extends problem_base { /** * Generate title for this problem. * * @return string Title of problem. */ public function title() { return 'Course categories tree structure'; } /** * Search for problems in the course categories. * * @uses $DB * @return array List of categories that contain missing parents or loops. */ public function find_problems() { global $DB; static $answer = null; if (is_null($answer)) { $categories = $DB->get_records('course_categories', array(), 'id'); // Look for missing parents. $missingparent = tool_health_category_find_missing_parents($categories); // Look for loops. $loops = tool_health_category_find_loops($categories); $answer = array($missingparent, $loops); } return $answer; } /** * Check if the problem exists. * * @return boolean True if either missing parents or loops found */ public function exists() { list($missingparent, $loops) = $this->find_problems(); return !empty($missingparent) || !empty($loops); } /** * Set problem severity. * * @return constant Problem severity. */ public function severity() { return SEVERITY_SIGNIFICANT; } /** * Generate problem description. * * @return string HTML containing details of the problem. */ public function description() { list($missingparent, $loops) = $this->find_problems(); $description = '
The course categories should be arranged into tree ' . ' structures by the course_categories.parent field. Sometimes ' . ' this tree structure gets messed up.
'; $description .= tool_health_category_list_missing_parents($missingparent); $description .= tool_health_category_list_loops($loops); return $description; } /** * Generate solution text. * * @uses $CFG * @return string HTML containing the suggested solution. */ public function solution() { global $CFG; list($missingparent, $loops) = $this->find_problems(); $solution = 'Consider executing the following SQL queries. These fix ' . 'the problem by moving some categories to the top level.
'; if (!empty($missingparent)) { $solution .= "UPDATE " . $CFG->prefix . "course_categories\n" . " SET parent = 0, depth = 1, path = CONCAT('/', id)\n" . " WHERE id IN (" . implode(',', array_keys($missingparent)) . ");\n"; } if (!empty($loops)) { $solution .= "
UPDATE " . $CFG->prefix . "course_categories\n" . " SET parent = 0, depth = 1, path = CONCAT('/', id)\n" . " WHERE id IN (" . implode(',', array_keys($loops)) . ");\n"; } return $solution; } } class problem_00000x extends problem_base { function title() { return ''; } function exists() { return false; } function severity() { return SEVERITY_SIGNIFICANT; } function description() { return ''; } function solution() { global $CFG; return ''; } } /* TODO: session.save_path -- it doesn't really matter because we are already IN a session, right? detect unsupported characters in $CFG->wwwroot - see bug Bug #6091 - relative vs absolute path during backup/restore process */