merging MOODLE_19_QUESTIONS with HEAD

This commit is contained in:
jamiesensei 2007-08-09 21:51:09 +00:00
parent 3bee1ead40
commit 271e6decda
47 changed files with 2807 additions and 1546 deletions

View file

@ -0,0 +1,72 @@
<?php // $Id$
require_once('../../../config.php');
require_once($CFG->dirroot.'/question/upgrade.php');
require_once($CFG->libdir.'/adminlib.php');
admin_externalpage_setup('reportquestion');
admin_externalpage_print_header();
print_heading(page_doc_link(get_string('adminreport', 'question')));
$probstr = '';
if ($CFG->version < 2007081000){
///cwrqpfs issue
$probstr = print_heading(get_string('cwrqpfs', 'question'), '', 3, 'main', true);
if ($updates = question_cwqpfs_to_update()){
$probstr .=('<p>'.get_string('cwrqpfsinfo', 'question').'</p>');
$probstr .= '<ul>';
$catlist = join(array_keys($updates), ',');
//get info about cateogries and no of questions used outside category's course
$categories = get_records_sql('SELECT qc.*, c.fullname as coursename FROM '.$CFG->prefix.'question_categories as qc, '
.$CFG->prefix.'course as c WHERE qc.course = c.id AND qc.id IN ('.$catlist.')');
foreach ($updates as $id => $publish){
$categories[$id]->caturl = "$CFG->wwwroot/question/category.php?sesskey=".sesskey().
"&amp;edit=$id&amp;courseid=".$categories[$id]->course;
if ($categories[$id]->publish){
$categories[$id]->changefrom = get_string('published', 'question');
$categories[$id]->changeto = get_string('unpublished', 'question');
} else {
$categories[$id]->changefrom = get_string('unpublished', 'question');
$categories[$id]->changeto = get_string('published', 'question');
}
$probstr .= '<li>'.get_string('changepublishstatuscat', 'question', $categories[$id]);
if ($questions = get_records_sql('SELECT q.*, qui.id as quizid, qui.name as quizname, cm.id as cmid, '
.'qui.course, c.fullname as coursename FROM '.$CFG->prefix.'question as q, '
.$CFG->prefix.'quiz_question_instances as qqi, '
.$CFG->prefix.'quiz as qui, '
.$CFG->prefix.'course_modules as cm, '
.$CFG->prefix.'modules as m, '
.$CFG->prefix.'course as c '
.'WHERE (q.category = '.$id.' AND qqi.question = q.id '
.'AND qqi.quiz = qui.id '
.'AND qui.course = c.id '
.'AND cm.instance = qui.id '
.'AND cm.module = m.id '
.'AND m.name = \'quiz\''
.'AND ('.$categories[$id]->course.' <> qui.course)) ORDER BY qui.id ASC')){
$probstr .= '<ul>';
foreach ($questions as $question){
$question->quizurl = "$CFG->wwwroot/mod/quiz/edit.php?cmid=".$question->cmid;
$question->qurl = "$CFG->wwwroot/question/question.php?cmid={$question->cmid}&amp;id={$question->id}&amp;returnurl=".urlencode($FULLME);
$probstr .= '<li>'.get_string('questionaffected', 'question', $question).'</li>';
}
$probstr .= '</ul>';
}
$probstr .= '</li>';
}
$probstr .= '</ul>';
} else {
$probstr .=('<p>'.get_string('cwrqpfsnoprob', 'question').'</p>');
}
}
if ($probstr) {
print_box($probstr);
} else {
print_box(get_string('noprobs', 'question'), 'boxwidthnarrow boxaligncenter generalbox');
}
admin_externalpage_print_footer();
?>

View file

@ -799,28 +799,6 @@ $moodle_capabilities = array(
) )
), ),
'moodle/question:import' => array(
'riskbitmask' => RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
)
),
'moodle/question:export' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
)
),
'moodle/question:managecategory' => array( 'moodle/question:managecategory' => array(
'captype' => 'write', 'captype' => 'write',
@ -831,17 +809,92 @@ $moodle_capabilities = array(
) )
), ),
'moodle/question:manage' => array( //new in moodle 1.9
'moodle/question:add' => array(
'riskbitmask' => RISK_XSS, 'riskbitmask' => RISK_SPAM,
'captype' => 'write', 'captype' => 'write',
'contextlevel' => CONTEXT_COURSE, 'contextlevel' => CONTEXT_COURSE,
'legacy' => array( 'legacy' => array(
'editingteacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW 'admin' => CAP_ALLOW
)
), ),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:editmine' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:editall' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:viewmine' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:viewall' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:usemine' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:useall' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:movemine' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
'moodle/question:moveall' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'legacy' => array(
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/question:manage'
),
//END new in moodle 1.9
// Configure the installed question types. // Configure the installed question types.
'moodle/question:config' => array( 'moodle/question:config' => array(

View file

@ -975,7 +975,6 @@ function xmldb_main_upgrade($oldversion=0) {
$result = $result && question_remove_rqp_qtype_config_string(); $result = $result && question_remove_rqp_qtype_config_string();
} }
if ($result && $oldversion < 2007072200) { if ($result && $oldversion < 2007072200) {
/// Remove obsoleted unit tests tables - they will be recreated automatically /// Remove obsoleted unit tests tables - they will be recreated automatically
$tables = array('grade_categories', $tables = array('grade_categories',
@ -1719,6 +1718,14 @@ function xmldb_main_upgrade($oldversion=0) {
} }
} }
*/ */
//need to change this when we merge with HEAD
if ($result && $oldversion < 2007081000) {
require_once($CFG->dirroot . '/question/upgrade.php');
$result = $result && question_upgrade_context_etc();
}
return $result; return $result;
} }
?> ?>

View file

@ -2,20 +2,22 @@
/** /**
* A moodle form field type for question categories. * A moodle form field type for question categories.
* *
* @copyright &copy; 2006 The Open University * @copyright Jamie Pratt
* @author T.J.Hunt@open.ac.uk * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodleforms * @package moodleforms
*//** */ */
global $CFG; global $CFG;
require_once("$CFG->libdir/form/select.php"); require_once("$CFG->libdir/form/selectgroups.php");
require_once("$CFG->libdir/questionlib.php");
/** /**
* HTML class for a drop down element to select a question category. * HTML class for a drop down element to select a question category.
* @access public * @access public
*/ */
class MoodleQuickForm_questioncategory extends MoodleQuickForm_select { class MoodleQuickForm_questioncategory extends MoodleQuickForm_selectgroups {
var $_options = array('top'=>false, 'currentcat'=>0, 'nochildrenof' => -1);
/** /**
* Constructor * Constructor
@ -28,45 +30,16 @@ class MoodleQuickForm_questioncategory extends MoodleQuickForm_select {
* @access public * @access public
* @return void * @return void
*/ */
function MoodleQuickForm_questioncategory($elementName = null, function MoodleQuickForm_questioncategory($elementName = null, $elementLabel = null, $options = null, $attributes = null) {
$elementLabel = null, $attributes = null, $options = null) { MoodleQuickForm_selectgroups::MoodleQuickForm_selectgroups($elementName, $elementLabel, array(), $attributes);
HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes, null);
global $COURSE;
$this->_type = 'questioncategory'; $this->_type = 'questioncategory';
if (!empty($options['courseid'])) { if (is_array($options)) {
$this->_courseid = $options['courseid']; $this->_options = $options + $this->_options;
} else { $this->loadArrayOptGroups(
$this->_courseid = $COURSE->id; question_category_options($this->_options['contexts'], $this->_options['top'], $this->_options['currentcat'],
} false, $this->_options['nochildrenof']));
if (!empty($options['published'])) {
$this->_published = $options['published'];
} else {
$this->_published = false;
}
if (!empty($options['only_editable'])) {
$this->_only_editable = $options['only_editable'];
} else {
$this->_only_editable = false;
} }
} }
/**
* Called by HTML_QuickForm whenever form event is made on this element
*
* @param string $event Name of event
* @param mixed $arg event arguments
* @param object $caller calling object
* @access public
* @return mixed
*/
function onQuickFormEvent($event, $arg, &$caller) {
switch ($event) {
case 'createElement':
$this->load(question_category_options($this->_courseid, $this->_published, $this->_only_editable));
break;
}
return parent::onQuickFormEvent($event, $arg, $caller);
}
} }
?> ?>

View file

@ -29,27 +29,54 @@
// When we backup a quiz we also need to backup the questions and possibly // When we backup a quiz we also need to backup the questions and possibly
// the data about student interaction with the questions. The functions to do // the data about student interaction with the questions. The functions to do
// that are included with the following library // that are included with the following library
require_once("$CFG->libdir/questionlib.php");
require_once("$CFG->dirroot/question/backuplib.php"); require_once("$CFG->dirroot/question/backuplib.php");
//STEP 1. Backup categories/questions and associated structures /*
// (course independent) * Insert necessary category ids to backup_ids table. Called during backup_check.html
*/
//Insert necessary category ids to backup_ids table function insert_category_and_question_ids($course, $backup_unique_code, $instances = null) {
function insert_category_ids($course, $backup_unique_code, $instances = null) {
global $CFG; global $CFG;
include_once("$CFG->dirroot/mod/quiz/lib.php");
// Create missing categories and reasign orphaned questions. // Create missing categories and reasign orphaned questions.
fix_orphaned_questions($course); fix_orphaned_questions($course);
// First, ALL categories from this course. // First, all categories from this course's context.
$coursecontext = get_context_instance(CONTEXT_COURSE, $course);
$status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids $status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids
(backup_code, table_name, old_id, info) (backup_code, table_name, old_id, info)
SELECT '$backup_unique_code', 'question_categories', qc.id, '' SELECT '$backup_unique_code', 'question_categories', qc.id, ''
FROM {$CFG->prefix}question_categories qc FROM {$CFG->prefix}question_categories qc
WHERE qc.course = $course", false); WHERE qc.contextid = {$coursecontext->id}", false);
// Then published categories from other courses used by the quizzes we are backing up.
// then, all categories from this course's modules' contexts.
// using 'dummykeyname' in sql because otherwise get_records_sql_menu returns an error
// if two key names are the same.
$cmcontexts = get_records_sql_menu("SELECT c.id, c.id AS dummykeyname FROM `{$CFG->prefix}modules` AS `mod`,
`{$CFG->prefix}course_modules` AS `cm`,
`{$CFG->prefix}context` as `c`
WHERE mod.name = 'quiz' AND mod.id = cm.module AND cm.id = c.instanceid
AND c.contextlevel = ".CONTEXT_MODULE." AND cm.course = $course");
if ($cmcontexts){
$status = $status && execute_sql("INSERT INTO {$CFG->prefix}backup_ids
(backup_code, table_name, old_id, info)
SELECT '$backup_unique_code', 'question_categories', qc.id, ''
FROM {$CFG->prefix}question_categories qc
WHERE qc.contextid IN (".join(array_keys($cmcontexts), ', ').")", false);
}
//put the ids of the questions from all these categories into the db.
$status = $status && execute_sql("INSERT INTO {$CFG->prefix}backup_ids
(backup_code, table_name, old_id, info)
SELECT '$backup_unique_code', 'question', q.id, ''
FROM {$CFG->prefix}question AS q, {$CFG->prefix}backup_ids AS bk
WHERE q.category = bk.old_id AND bk.table_name = 'question_categories' AND
bk.backup_code = '$backup_unique_code'", false);
// Then categories from parent contexts used by the quizzes we are backing up.
//TODO this will need generalising when we have modules other than quiz using shared questions above course level.
$parentcontexts = get_parent_contexts($coursecontext);
$from = "{$CFG->prefix}quiz quiz,";
$where = "AND quiz.course = '$course'
AND qqi.quiz = quiz.id";
if (!empty($instances) && is_array($instances) && count($instances)) { if (!empty($instances) && is_array($instances) && count($instances)) {
$questionselectsqlfrom = ''; $questionselectsqlfrom = '';
$questionselectsqlwhere = 'AND qqi.quiz IN ('.implode(',',array_keys($instances)).')'; $questionselectsqlwhere = 'AND qqi.quiz IN ('.implode(',',array_keys($instances)).')';
@ -61,7 +88,7 @@
$categoriesinothercourses = get_records_sql(" $categoriesinothercourses = get_records_sql("
SELECT id, parent, 0 AS childrendone SELECT id, parent, 0 AS childrendone
FROM {$CFG->prefix}question_categories FROM {$CFG->prefix}question_categories
WHERE course <> $course WHERE contextid IN (".join($parentcontexts, ', ').")
AND id IN ( AND id IN (
SELECT DISTINCT question.category SELECT DISTINCT question.category
FROM {$CFG->prefix}question question, FROM {$CFG->prefix}question question,
@ -70,21 +97,35 @@
WHERE qqi.question = question.id WHERE qqi.question = question.id
$questionselectsqlwhere $questionselectsqlwhere
)", false); )", false);
if (!$categoriesinothercourses) { if (!$categories) {
$categoriesinothercourses = array(); $categories = array();
} } else {
//put the ids of the used questions from all these categories into the db.
$status = $status && execute_sql("INSERT INTO {$CFG->prefix}backup_ids
(backup_code, table_name, old_id, info)
SELECT '$backup_unique_code', 'question', q.id, ''
FROM {$CFG->prefix}question q,
$from
{$CFG->prefix}question_categories qc,
{$CFG->prefix}quiz_question_instances qqi
WHERE (qqi.question = q.id
OR qqi.question = q.parent)
AND q.category = qc.id
AND qc.contextid IN (".join($parentcontexts, ', ').")
$where", false);
// Add the parent categories, of these categories up to the top of the category tree. // Add the parent categories, of these categories up to the top of the category tree.
foreach ($categoriesinothercourses as $category) { // not backing up the questions in these categories.
foreach ($categories as $category) {
while ($category->parent != 0) { while ($category->parent != 0) {
if (array_key_exists($category->parent, $categoriesinothercourses)) { if (array_key_exists($category->parent, $categories)) {
// Parent category already on the list. // Parent category already on the list.
break; break;
} }
$currentid = $category->id; $currentid = $category->id;
$category = get_record('question_categories', 'id', $category->parent, '', '', '', '', 'id, parent, 0 AS childrendone'); $category = get_record('question_categories', 'id', $category->parent, '', '', '', '', 'id, parent, 0 AS childrendone');
if ($category) { if ($category) {
$categoriesinothercourses[$category->id] = $category; $categories[$category->id] = $category;
} else { } else {
// Parent not found: this indicates an error, but just fix it. // Parent not found: this indicates an error, but just fix it.
set_field('question_categories', 'parent', 0, 'id', $currentid); set_field('question_categories', 'parent', 0, 'id', $currentid);
@ -93,40 +134,53 @@
} }
} }
// Now we look for random questions used in our quizzes // Now we look for categories from other courses containing random questions
// that select from subcategories in other courses. That implies // in our quizzes that select from the category and its subcategories. That implies
// those subcategories also need to be backed up. (The categories themselves // those subcategories also need to be backed up. (The categories themselves
// and their parents will already have been included.) // and their parents will already have been included.)
$categorieswithrandom = get_records_sql(" $categorieswithrandom = get_records_sql("
SELECT DISTINCT question.category AS id SELECT question.category AS id, SUM(question.questiontext) as questiontext
FROM {$CFG->prefix}quiz_question_instances qqi, FROM {$CFG->prefix}quiz_question_instances qqi,
$questionselectsqlfrom $from
{$CFG->prefix}question question {$CFG->prefix}question question
WHERE question.id = qqi.question WHERE question.id = qqi.question
AND question.qtype = '" . RANDOM . "' AND question.qtype = '" . RANDOM . "'
AND question.questiontext = '1' $where
$questionselectsqlwhere GROUP BY question.category
"); ");
$randomselectedquestions = array();
if ($categorieswithrandom) { if ($categorieswithrandom) {
foreach ($categorieswithrandom as $category) { foreach ($categorieswithrandom as $category) {
if (isset($categoriesinothercourses[$category->id])){ if ($category->questiontext){
$status = quiz_backup_add_sub_categories($categoriesinothercourses, $category->id); $status = $status && quiz_backup_add_sub_categories($categories, $randomselectedquestions, $category->id);
} }
} }
$returnval = get_records_sql("
SELECT question.id
FROM {$CFG->prefix}question question
WHERE question.category IN (".join(array_keys($categorieswithrandom), ', ').")");
if ($returnval) {
$randomselectedquestions += $returnval;
}
} }
// Finally, add all these extra categories to the backup_ids table. // Finally, add all these extra categories to the backup_ids table.
foreach ($categoriesinothercourses as $category) { foreach ($categories as $category) {
$status = $status && backup_putid($backup_unique_code, 'question_categories', $category->id, 0); $status = $status && backup_putid($backup_unique_code, 'question_categories', $category->id, 0);
} }
// Finally, add all these extra categories to the backup_ids table.
foreach ($randomselectedquestions as $question) {
$status = $status && backup_putid($backup_unique_code, 'question', $question->id, 0);
}
}
return $status; return $status;
} }
/** /**
* Helper function adding the id of all the subcategories of a category to an array. * Helper function adding the id of all the subcategories of a category to an array.
*/ */
function quiz_backup_add_sub_categories(&$categories, $categoryid) { function quiz_backup_add_sub_categories(&$categories, &$questions, $categoryid) {
global $CFG;
$status = true; $status = true;
if ($categories[$categoryid]->childrendone) { if ($categories[$categoryid]->childrendone) {
return $status; return $status;
@ -136,7 +190,16 @@
if (!array_key_exists($subcategory->id, $categories)) { if (!array_key_exists($subcategory->id, $categories)) {
$categories[$subcategory->id] = $subcategory; $categories[$subcategory->id] = $subcategory;
} }
$status = $status && quiz_backup_add_sub_categories($categories, $subcategory->id); $status = $status && quiz_backup_add_sub_categories($categories, $questions, $subcategory->id);
}
$subcatlist = join(array_keys($subcategories), ',');
$returnval = get_records_sql("
SELECT question.id
FROM {$CFG->prefix}question question
WHERE question.category IN ($subcatlist)
");
if ($returnval) {
$questions += $returnval;
} }
} }
$categories[$categoryid]->childrendone = 1; $categories[$categoryid]->childrendone = 1;
@ -166,10 +229,10 @@
if (!$exist) { if (!$exist) {
//Build a new category //Build a new category
$db_cat = new stdClass; $db_cat = new stdClass;
$db_cat->course = $course; // always create missing categories in course context
$db_cat->contextid = get_context_instance(CONTEXT_COURSE, $course);
$db_cat->name = get_string('recreatedcategory','',$key); $db_cat->name = get_string('recreatedcategory','',$key);
$db_cat->info = get_string('recreatedcategory','',$key); $db_cat->info = get_string('recreatedcategory','',$key);
$db_cat->publish = 1;
$db_cat->stamp = make_unique_id_code(); $db_cat->stamp = make_unique_id_code();
//Insert the new category //Insert the new category
$catid = insert_record('question_categories',$db_cat); $catid = insert_record('question_categories',$db_cat);
@ -453,11 +516,14 @@
////Return an array of info (name,value) ////Return an array of info (name,value)
/// $instances is an array with key = instanceid, value = object (name,id,userdata) /// $instances is an array with key = instanceid, value = object (name,id,userdata)
function quiz_check_backup_mods($course,$user_data= false,$backup_unique_code,$instances=null) { function quiz_check_backup_mods($course,$user_data= false,$backup_unique_code,$instances=null) {
//Deletes data from mdl_backup_ids (categories section) //Deletes data from mdl_backup_ids (categories section)
delete_category_ids ($backup_unique_code); delete_ids ($backup_unique_code, 'question_categories');
//Create date into mdl_backup_ids (categories section) delete_ids ($backup_unique_code, 'question');
insert_category_ids ($course,$backup_unique_code,$instances); //this function selects all the questions / categories to be backed up.
insert_category_and_question_ids($course, $backup_unique_code, $instances);
if ($course != SITEID){
question_insert_site_file_names($course, $backup_unique_code);
}
if (!empty($instances) && is_array($instances) && count($instances)) { if (!empty($instances) && is_array($instances) && count($instances)) {
$info = array(); $info = array();
foreach ($instances as $id => $instance) { foreach ($instances as $id => $instance) {

View file

@ -1,17 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/quiz/db" VERSION="20070228" COMMENT="XMLDB file for Moodle mod/quiz" <XMLDB PATH="mod/quiz/db" VERSION="20070522" COMMENT="XMLDB file for Moodle mod/quiz"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
> >
<TABLES> <TABLES>
<TABLE NAME="question_categories" COMMENT="Categories are for grouping questions" NEXT="question"> <TABLE NAME="question_categories" COMMENT="Categories are for grouping questions" NEXT="question">
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="course"/> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="name"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="id" NEXT="name"/> <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="id" NEXT="contextid"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="course" NEXT="info"/> <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="context that this category is shared in" PREVIOUS="name" NEXT="info"/>
<FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="name" NEXT="publish"/> <FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="contextid" NEXT="stamp"/>
<FIELD NAME="publish" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="info" NEXT="stamp"/> <FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="info" NEXT="parent"/>
<FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="publish" NEXT="parent"/>
<FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="stamp" NEXT="sortorder"/> <FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="stamp" NEXT="sortorder"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" ENUM="false" PREVIOUS="parent"/> <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" ENUM="false" PREVIOUS="parent"/>
</FIELDS> </FIELDS>
@ -20,7 +19,7 @@
<KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="question_categories" REFFIELDS="id" COMMENT="note that to make this recursive FK working someday, the parent field must be declared NULL" PREVIOUS="primary"/> <KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="question_categories" REFFIELDS="id" COMMENT="note that to make this recursive FK working someday, the parent field must be declared NULL" PREVIOUS="primary"/>
</KEYS> </KEYS>
<INDEXES> <INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/> <INDEX NAME="contextid" UNIQUE="false" FIELDS="contextid" COMMENT="links to context table"/>
</INDEXES> </INDEXES>
</TABLE> </TABLE>
<TABLE NAME="question" COMMENT="The questions themselves" PREVIOUS="question_categories" NEXT="question_answers"> <TABLE NAME="question" COMMENT="The questions themselves" PREVIOUS="question_categories" NEXT="question_answers">
@ -39,12 +38,18 @@
<FIELD NAME="length" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" ENUM="false" PREVIOUS="qtype" NEXT="stamp"/> <FIELD NAME="length" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" ENUM="false" PREVIOUS="qtype" NEXT="stamp"/>
<FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="length" NEXT="version"/> <FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="length" NEXT="version"/>
<FIELD NAME="version" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="stamp" NEXT="hidden"/> <FIELD NAME="version" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="stamp" NEXT="hidden"/>
<FIELD NAME="hidden" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="version"/> <FIELD NAME="hidden" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="version" NEXT="timecreated"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="time question was created" PREVIOUS="hidden" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="time that question was last modified" PREVIOUS="timecreated" NEXT="createdby"/>
<FIELD NAME="createdby" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="userid of person who created this question" PREVIOUS="timemodified" NEXT="modifiedby"/>
<FIELD NAME="modifiedby" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="userid of person who last edited this question" PREVIOUS="createdby"/>
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for question" NEXT="category"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for question" NEXT="category"/>
<KEY NAME="category" TYPE="foreign" FIELDS="category" REFTABLE="question_categories" REFFIELDS="id" PREVIOUS="primary" NEXT="parent"/> <KEY NAME="category" TYPE="foreign" FIELDS="category" REFTABLE="question_categories" REFFIELDS="id" PREVIOUS="primary" NEXT="parent"/>
<KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="question" REFFIELDS="id" COMMENT="note that to make this recursive FK working someday, the parent field must be declared NULL" PREVIOUS="category"/> <KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="question" REFFIELDS="id" COMMENT="note that to make this recursive FK working someday, the parent field must be declared NULL" PREVIOUS="category" NEXT="createdby"/>
<KEY NAME="createdby" TYPE="foreign" FIELDS="createdby" REFTABLE="user" REFFIELDS="id" COMMENT="foreign (createdby) references user (id)" PREVIOUS="parent" NEXT="modifiedby"/>
<KEY NAME="modifiedby" TYPE="foreign" FIELDS="modifiedby" REFTABLE="user" REFFIELDS="id" COMMENT="foreign (modifiedby) references user (id)" PREVIOUS="createdby"/>
</KEYS> </KEYS>
</TABLE> </TABLE>
<TABLE NAME="question_answers" COMMENT="Answers, with a fractional grade (0-1) and feedback" PREVIOUS="question" NEXT="question_dataset_definitions"> <TABLE NAME="question_answers" COMMENT="Answers, with a fractional grade (0-1) and feedback" PREVIOUS="question" NEXT="question_dataset_definitions">

View file

@ -19,7 +19,7 @@
function xmldb_quiz_upgrade($oldversion=0) { function xmldb_quiz_upgrade($oldversion=0) {
global $CFG, $THEME, $db; global $CFG, $THEME;
$result = true; $result = true;

View file

@ -30,12 +30,12 @@
require_once($CFG->dirroot.'/mod/quiz/editlib.php'); require_once($CFG->dirroot.'/mod/quiz/editlib.php');
/** /**
* Callback function called from question_list() function (which is called from showbank() * Callback function called from question_list() function (which is called from showbank())
* Displays action icon as first action for each question. * Displays action icon as first action for each question.
*/ */
function module_specific_actions($pageurl, $questionid, $cmid){ function module_specific_actions($pageurl, $questionid, $cmid, $canuse){
global $CFG; global $CFG;
if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){ if ($canuse){
$straddtoquiz = get_string("addtoquiz", "quiz"); $straddtoquiz = get_string("addtoquiz", "quiz");
$out = "<a title=\"$straddtoquiz\" href=\"edit.php?".$pageurl->get_query_string()."&amp;addquestion=$questionid&amp;sesskey=".sesskey()."\"><img $out = "<a title=\"$straddtoquiz\" href=\"edit.php?".$pageurl->get_query_string()."&amp;addquestion=$questionid&amp;sesskey=".sesskey()."\"><img
src=\"$CFG->pixpath/t/moveleft.gif\" alt=\"$straddtoquiz\" /></a>&nbsp;"; src=\"$CFG->pixpath/t/moveleft.gif\" alt=\"$straddtoquiz\" /></a>&nbsp;";
@ -45,28 +45,24 @@
} }
} }
/** /**
* Callback function called from question_list() function (which is called from showbank() * Callback function called from question_list() function (which is called from showbank())
* Displays button in form with checkboxes for each question. * Displays button in form with checkboxes for each question.
*/ */
function module_specific_buttons($cmid){ function module_specific_buttons($cmid){
global $THEME; global $THEME;
if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
$straddtoquiz = get_string("addtoquiz", "quiz"); $straddtoquiz = get_string("addtoquiz", "quiz");
$out = "<input type=\"submit\" name=\"add\" value=\"{$THEME->larrow} $straddtoquiz\" />\n"; $out = "<input type=\"submit\" name=\"add\" value=\"{$THEME->larrow} $straddtoquiz\" />\n";
$out .= '</td><td>'; echo '<br />';
return $out; return $out;
} else {
return '';
}
} }
/** /**
* Callback function called from question_list() function (which is called from showbank() * Callback function called from question_list() function (which is called from showbank())
* Displays button in form with checkboxes for each question.
*/ */
function module_specific_controls($totalnumber, $recurse, $categoryid, $cmid){ function module_specific_controls($totalnumber, $recurse, $category, $cmid){
if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){ $catcontext = get_context_instance_by_id($category->contextid);
if (has_capability('moodle/question:useall', $catcontext)){
for ($i = 1;$i <= min(10, $totalnumber); $i++) { for ($i = 1;$i <= min(10, $totalnumber); $i++) {
$randomcount[$i] = $i; $randomcount[$i] = $i;
} }
@ -76,16 +72,16 @@
$out = '<br />'; $out = '<br />';
$out .= get_string('addrandom', 'quiz', choose_from_menu($randomcount, 'randomcount', '1', '', '', '', true)); $out .= get_string('addrandom', 'quiz', choose_from_menu($randomcount, 'randomcount', '1', '', '', '', true));
$out .= '<input type="hidden" name="recurse" value="'.$recurse.'" />'; $out .= '<input type="hidden" name="recurse" value="'.$recurse.'" />';
$out .= "<input type=\"hidden\" name=\"categoryid\" value=\"$categoryid\" />"; $out .= "<input type=\"hidden\" name=\"categoryid\" value=\"$category->id\" />";
$out .= ' <input type="submit" name="addrandom" value="'. get_string('add') .'" />'; $out .= ' <input type="submit" name="addrandom" value="'. get_string('add') .'" />';
$out .= helpbutton('random', get_string('random', 'quiz'), 'quiz', true, false, '', true); $out .= helpbutton('random', get_string('random', 'quiz'), 'quiz', true, false, '', true);
return $out;
} else { } else {
return ''; $out = '';
} }
return $out;
} }
list($thispageurl, $courseid, $cmid, $cm, $quiz, $pagevars) = question_edit_setup(true); list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) = question_edit_setup('editq', true);
//these params are only passed from page request to request while we stay on this page //these params are only passed from page request to request while we stay on this page
//otherwise they would go in question_edit_setup //otherwise they would go in question_edit_setup
@ -112,15 +108,14 @@
if (! $course = get_record("course", "id", $quiz->course)) { if (! $course = get_record("course", "id", $quiz->course)) {
error("This course doesn't exist"); error("This course doesn't exist");
} }
$coursecontext = get_context_instance(CONTEXT_COURSE, $quiz->course);
$quizcontext = get_context_instance(CONTEXT_MODULE, $quiz->cmid);
// Log this visit. // Log this visit.
add_to_log($cm->course, 'quiz', 'editquestions', add_to_log($cm->course, 'quiz', 'editquestions',
"view.php?id=$cm->id", "$quiz->id", $cm->id); "view.php?id=$cm->id", "$quiz->id", $cm->id);
require_capability('mod/quiz:manage', $quizcontext); //you need mod/quiz:manage in addition to question capabilities to access this page.
require_capability('mod/quiz:manage', $contexts->lowest());
if (isset($quiz->instance) if (isset($quiz->instance)
&& empty($quiz->grades)){ // Construct an array to hold all the grades. && empty($quiz->grades)){ // Construct an array to hold all the grades.
@ -187,6 +182,8 @@
if (! $category = get_record('question_categories', 'id', $categoryid)) { if (! $category = get_record('question_categories', 'id', $categoryid)) {
error('Category ID is incorrect'); error('Category ID is incorrect');
} }
$catcontext = get_context_instance_by_id($category->contextid);
require_capability('moodle/question:useall', $catcontext);
$category->name = addslashes($category->name); $category->name = addslashes($category->name);
// find existing random questions in this category // find existing random questions in this category
$random = RANDOM; $random = RANDOM;
@ -214,7 +211,7 @@
if ($randomcreate > 0) { if ($randomcreate > 0) {
$form->name = get_string('random', 'quiz') .' ('. $category->name .')'; $form->name = get_string('random', 'quiz') .' ('. $category->name .')';
$form->category = $category->id; $form->category = "$category->id,$category->contextid";
$form->questiontext = $recurse; // we use the questiontext field to store the info $form->questiontext = $recurse; // we use the questiontext field to store the info
// on whether to include questions in subcategories // on whether to include questions in subcategories
$form->questiontextformat = 0; $form->questiontextformat = 0;
@ -246,7 +243,6 @@
error('Could not save layout'); error('Could not save layout');
} }
} }
if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz
quiz_delete_quiz_question($_REQUEST['delete'], $quiz); quiz_delete_quiz_question($_REQUEST['delete'], $quiz);
} }
@ -296,18 +292,15 @@
delete_records('quiz_attempts', 'preview', '1', 'quiz', $quiz->id); delete_records('quiz_attempts', 'preview', '1', 'quiz', $quiz->id);
} }
/// all commands have been dealt with, now print the page question_showbank_actions($thispageurl, $cm);
if (empty($quiz->category) or !record_exists('question_categories', 'id', $quiz->category)) { /// all commands have been dealt with, now print the page
$category = get_default_question_category($course->id);
$quiz->category = $category->id;
}
// Print basic page layout. // Print basic page layout.
if (isset($quiz->instance) and record_exists_select('quiz_attempts', "quiz = '$quiz->instance' AND preview = '0'")){ if (isset($quiz->instance) and record_exists_select('quiz_attempts', "quiz = '$quiz->instance' AND preview = '0'")){
// one column layout with table of questions used in this quiz // one column layout with table of questions used in this quiz
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext) $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz')) ? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
: ""; : "";
$navlinks = array(); $navlinks = array();
@ -329,9 +322,7 @@
$a->attemptnum = count_records('quiz_attempts', 'quiz', $quiz->id, 'preview', 0); $a->attemptnum = count_records('quiz_attempts', 'quiz', $quiz->id, 'preview', 0);
$a->studentnum = count_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'", 'COUNT(DISTINCT userid)'); $a->studentnum = count_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'", 'COUNT(DISTINCT userid)');
$a->studentstring = $course->students; $a->studentstring = $course->students;
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->instance, $course->id)) {
error("Course Module ID was incorrect");
}
echo "<div class=\"attemptsnotice\">\n"; echo "<div class=\"attemptsnotice\">\n";
echo "<a href=\"report.php?mode=overview&amp;id=$cm->id\">".get_string('numattempts', 'quiz', $a)."</a><br />".get_string("attemptsexist","quiz"); echo "<a href=\"report.php?mode=overview&amp;id=$cm->id\">".get_string('numattempts', 'quiz', $a)."</a><br />".get_string("attemptsexist","quiz");
echo "</div><br />\n"; echo "</div><br />\n";
@ -347,7 +338,7 @@
} }
// two column layout with quiz info in left column // two column layout with quiz info in left column
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext) $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz')) ? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
: ""; : "";
$navlinks = array(); $navlinks = array();
@ -377,7 +368,7 @@
echo '</td><td style="width:50%" valign="top">'; echo '</td><td style="width:50%" valign="top">';
question_showbank($thispageurl, $cm, $pagevars['qpage'], $pagevars['qperpage'], $pagevars['qsortorder'], $pagevars['qsortorderdecoded'], question_showbank('editq', $contexts, $thispageurl, $cm, $pagevars['qpage'], $pagevars['qperpage'], $pagevars['qsortorder'], $pagevars['qsortorderdecoded'],
$pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['showquestiontext']); $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['showquestiontext']);
echo '</td></tr>'; echo '</td></tr>';

View file

@ -141,6 +141,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
$strgrade = get_string("grade"); $strgrade = get_string("grade");
$strremove = get_string('remove', 'quiz'); $strremove = get_string('remove', 'quiz');
$stredit = get_string("edit"); $stredit = get_string("edit");
$strview = get_string("view");
$straction = get_string("action"); $straction = get_string("action");
$strmoveup = get_string("moveup"); $strmoveup = get_string("moveup");
$strmovedown = get_string("movedown"); $strmovedown = get_string("movedown");
@ -155,7 +156,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
return 0; return 0;
} }
if (!$questions = get_records_sql("SELECT q.*,c.course if (!$questions = get_records_sql("SELECT q.*,c.contextid
FROM {$CFG->prefix}question q, FROM {$CFG->prefix}question q,
{$CFG->prefix}question_categories c {$CFG->prefix}question_categories c
WHERE q.id in ($quiz->questions) WHERE q.id in ($quiz->questions)
@ -169,7 +170,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
$count = 0; $count = 0;
$qno = 1; $qno = 1;
$sumgrade = 0; $sumgrade = 0;
$order = explode(",", $quiz->questions); $order = explode(',', $quiz->questions);
$lastindex = count($order)-1; $lastindex = count($order)-1;
// If the list does not end with a pagebreak then add it on. // If the list does not end with a pagebreak then add it on.
if ($order[$lastindex] != 0) { if ($order[$lastindex] != 0) {
@ -235,12 +236,11 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
echo '<td colspan="2">&nbsp;</td>'; echo '<td colspan="2">&nbsp;</td>';
} }
$count++; $count++;
// missing </tr> here, if loop is broken, need to close the </tr> from line 199/201 // missing </tr> here, if loop is broken, need to close the </tr>
echo "</tr>"; echo "</tr>";
continue; continue;
} }
$question = $questions[$qnum]; $question = $questions[$qnum];
$canedit = has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $question->course));
echo "<td>"; echo "<td>";
if ($count != 0) { if ($count != 0) {
@ -276,17 +276,20 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
} }
echo '</td><td align="center">'; echo '</td><td align="center">';
if ($question->qtype != 'random') { if (($question->qtype != 'random')){
quiz_question_preview_button($quiz, $question); echo quiz_question_preview_button($quiz, $question);
} }
if ($canedit) {
$returnurl = $pageurl->out(); $returnurl = $pageurl->out();
$questionparams = array('returnurl' => $returnurl, 'cmid'=>$quiz->cmid, 'id' => $qnum); $questionparams = array('returnurl' => $returnurl, 'cmid'=>$quiz->cmid, 'id' => $question->id);
$questionurl = new moodle_url("$CFG->wwwroot/question/question.php", $questionparams); $questionurl = new moodle_url("$CFG->wwwroot/question/question.php", $questionparams);
if (question_has_capability_on($question, 'edit', $question->category) || question_has_capability_on($question, 'move', $question->category)) {
echo "<a title=\"$stredit\" href=\"".$questionurl->out()."\"> echo "<a title=\"$stredit\" href=\"".$questionurl->out()."\">
<img src=\"$CFG->pixpath/t/edit.gif\" class=\"iconsmall\" alt=\"$stredit\" /></a>"; <img src=\"$CFG->pixpath/t/edit.gif\" class=\"iconsmall\" alt=\"$stredit\" /></a>";
} elseif (question_has_capability_on($question, 'view', $question->category)){
echo "<a title=\"$strview\" href=\"".$questionurl->out(false, array('id'=>$question->id))."\"><img
src=\"$CFG->pixpath/i/info.gif\" alt=\"$strview\" /></a>&nbsp;";
} }
if ($allowdelete) { if ($allowdelete && question_has_capability_on($question, 'use', $question->category)) { // remove from quiz, not question delete.
echo "<a title=\"$strremove\" href=\"".$pageurl->out_action(array('delete'=>$count))."\"> echo "<a title=\"$strremove\" href=\"".$pageurl->out_action(array('delete'=>$count))."\">
<img src=\"$CFG->pixpath/t/removeright.gif\" class=\"iconsmall\" alt=\"$strremove\" /></a>"; <img src=\"$CFG->pixpath/t/removeright.gif\" class=\"iconsmall\" alt=\"$strremove\" /></a>";
} }

View file

@ -21,7 +21,8 @@
// Print the header // Print the header
$strquizzes = get_string("modulenameplural", "quiz"); $strquizzes = get_string("modulenameplural", "quiz");
$streditquestions = ''; $streditquestions = '';
if (has_capability('moodle/question:manage', $coursecontext)) { $editqcontexts = new question_edit_contexts($coursecontext);
if ($editqcontexts->have_one_edit_tab_cap('questions')) {
$streditquestions = $streditquestions =
"<form target=\"_parent\" method=\"get\" action=\"$CFG->wwwroot/question/edit.php\"> "<form target=\"_parent\" method=\"get\" action=\"$CFG->wwwroot/question/edit.php\">
<div> <div>

View file

@ -662,6 +662,9 @@ function quiz_upgrade_states($attempt) {
*/ */
function quiz_question_preview_button($quiz, $question) { function quiz_question_preview_button($quiz, $question) {
global $CFG; global $CFG;
if (!question_has_capability_on($question, 'use', $question->category)){
return '';
}
$strpreview = get_string('previewquestion', 'quiz'); $strpreview = get_string('previewquestion', 'quiz');
return link_to_popup_window('/question/preview.php?id=' . $question->id . '&amp;quizid=' . $quiz->id, 'questionpreview', return link_to_popup_window('/question/preview.php?id=' . $question->id . '&amp;quizid=' . $quiz->id, 'questionpreview',
"<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />", "<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />",

View file

@ -16,12 +16,13 @@ if (!isset($currenttab)) {
if (!isset($cm)) { if (!isset($cm)) {
$cm = get_coursemodule_from_instance('quiz', $quiz->id); $cm = get_coursemodule_from_instance('quiz', $quiz->id);
} }
if (!isset($course)) {
$course = get_record('course', 'id', $quiz->course);
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id); $context = get_context_instance(CONTEXT_MODULE, $cm->id);
if (!isset($contexts)){
$contexts = new question_edit_contexts($context);
}
$tabs = array(); $tabs = array();
$row = array(); $row = array();
$inactive = array(); $inactive = array();
@ -36,7 +37,7 @@ if (has_capability('mod/quiz:viewreports', $context)) {
if (has_capability('mod/quiz:preview', $context)) { if (has_capability('mod/quiz:preview', $context)) {
$row[] = new tabobject('preview', "$CFG->wwwroot/mod/quiz/attempt.php?q=$quiz->id", get_string('preview', 'quiz')); $row[] = new tabobject('preview', "$CFG->wwwroot/mod/quiz/attempt.php?q=$quiz->id", get_string('preview', 'quiz'));
} }
if (has_capability('mod/quiz:manage', $context)) { if ($contexts->have_one_edit_tab_cap('editq')) {
$row[] = new tabobject('edit', "$CFG->wwwroot/mod/quiz/edit.php?cmid=$cm->id", get_string('edit')); $row[] = new tabobject('edit', "$CFG->wwwroot/mod/quiz/edit.php?cmid=$cm->id", get_string('edit'));
} }
@ -83,9 +84,12 @@ if ($currenttab == 'edit' and isset($mode)) {
$streditingquiz = get_string("editinga", "moodle", $strquiz); $streditingquiz = get_string("editinga", "moodle", $strquiz);
$strupdate = get_string('updatethis', 'moodle', $strquiz); $strupdate = get_string('updatethis', 'moodle', $strquiz);
if ($contexts->have_one_edit_tab_cap('editq')) {
$row[] = new tabobject('editq', "$CFG->wwwroot/mod/quiz/edit.php?".$thispageurl->get_query_string(), $strquiz, $streditingquiz); $row[] = new tabobject('editq', "$CFG->wwwroot/mod/quiz/edit.php?".$thispageurl->get_query_string(), $strquiz, $streditingquiz);
questionbank_navigation_tabs($row, $context, $thispageurl->get_query_string()); }
questionbank_navigation_tabs($row, $contexts, $thispageurl->get_query_string());
$tabs[] = $row; $tabs[] = $row;
} }
print_tabs($tabs, $currenttab, $inactive, $activated); print_tabs($tabs, $currenttab, $inactive, $activated);

View file

@ -77,6 +77,42 @@
require_once("$CFG->libdir/questionlib.php"); require_once("$CFG->libdir/questionlib.php");
function backup_question_category_context($bf, $contextid, $course) {
$status = true;
$context = get_context_instance_by_id($contextid);
$status = $status && fwrite($bf,start_tag("CONTEXT",4,true));
switch ($context->contextlevel){
case CONTEXT_MODULE:
$status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'module'));
$status = $status && fwrite($bf,full_tag("INSTANCE",5,false, $context->instanceid));
break;
case CONTEXT_COURSE:
$status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'course'));
break;
case CONTEXT_COURSECAT:
$thiscourse = get_record('course', 'id', $course);
$cat = $thiscourse->category;
$catno = 1;
while($context->instanceid != $cat){
$catno ++;
if ($cat ==0) {
return false;
}
$cat = get_field('course_categories', 'parent', 'id', $cat);
}
$status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'coursecategory'));
$status = $status && fwrite($bf,full_tag("COURSECATEGORYLEVEL",5,false, $catno));
break;
case CONTEXT_SYSTEM:
$status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'system'));
break;
default :
return false;
}
$status = $status && fwrite($bf,end_tag("CONTEXT",4,true));
return $status;
}
function backup_question_categories($bf,$preferences) { function backup_question_categories($bf,$preferences) {
global $CFG; global $CFG;
@ -89,28 +125,28 @@
//If we've categories //If we've categories
if ($categories) { if ($categories) {
//Write start tag //Write start tag
$status = fwrite($bf,start_tag("QUESTION_CATEGORIES",2,true)); $status = $status && fwrite($bf,start_tag("QUESTION_CATEGORIES",2,true));
//Iterate over each category //Iterate over each category
foreach ($categories as $cat) { foreach ($categories as $cat) {
//Start category //Start category
$status = fwrite ($bf,start_tag("QUESTION_CATEGORY",3,true)); $status = $status && fwrite ($bf,start_tag("QUESTION_CATEGORY",3,true));
//Get category data from question_categories //Get category data from question_categories
$category = get_record ("question_categories","id",$cat->old_id); $category = get_record ("question_categories","id",$cat->old_id);
//Print category contents //Print category contents
fwrite($bf,full_tag("ID",4,false,$category->id)); $status = $status && fwrite($bf,full_tag("ID",4,false,$category->id));
fwrite($bf,full_tag("NAME",4,false,$category->name)); $status = $status && fwrite($bf,full_tag("NAME",4,false,$category->name));
fwrite($bf,full_tag("INFO",4,false,$category->info)); $status = $status && fwrite($bf,full_tag("INFO",4,false,$category->info));
fwrite($bf,full_tag("PUBLISH",4,false,$category->publish)); $status = $status && backup_question_category_context($bf, $category->contextid, $preferences->backup_course);
fwrite($bf,full_tag("STAMP",4,false,$category->stamp)); $status = $status && fwrite($bf,full_tag("STAMP",4,false,$category->stamp));
fwrite($bf,full_tag("PARENT",4,false,$category->parent)); $status = $status && fwrite($bf,full_tag("PARENT",4,false,$category->parent));
fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder)); $status = $status && fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder));
//Now, backup their questions //Now, backup their questions
$status = backup_question($bf,$preferences,$category->id); $status = $status && backup_question($bf,$preferences,$category->id);
//End category //End category
$status = fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true)); $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true));
} }
//Write end tag //Write end tag
$status = fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true)); $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true));
} }
return $status; return $status;
@ -127,16 +163,21 @@
// We'll fetch the questions sorted by parent so that questions with no parents // We'll fetch the questions sorted by parent so that questions with no parents
// (these are the ones which could be parents themselves) are backed up first. This // (these are the ones which could be parents themselves) are backed up first. This
// is important for the recoding of the parent field during the restore process // is important for the recoding of the parent field during the restore process
$questions = get_records("question","category",$category,"parent ASC, id"); // Only select questions with ids in backup_ids table
$questions = get_records_sql("SELECT q.* FROM {$CFG->prefix}backup_ids bk, {$CFG->prefix}question q ".
"WHERE q.category= $category AND ".
"bk.old_id=q.id AND ".
"bk.backup_code = {$preferences->backup_unique_code} ".
"ORDER BY parent ASC, id");
//If there are questions //If there are questions
if ($questions) { if ($questions) {
//Write start tag //Write start tag
$status = fwrite ($bf,start_tag("QUESTIONS",$level,true)); $status = $status && fwrite ($bf,start_tag("QUESTIONS",$level,true));
$counter = 0; $counter = 0;
//Iterate over each question //Iterate over each question
foreach ($questions as $question) { foreach ($questions as $question) {
//Start question //Start question
$status = fwrite ($bf,start_tag("QUESTION",$level + 1,true)); $status = $status && fwrite ($bf,start_tag("QUESTION",$level + 1,true));
//Print question contents //Print question contents
fwrite ($bf,full_tag("ID",$level + 2,false,$question->id)); fwrite ($bf,full_tag("ID",$level + 2,false,$question->id));
fwrite ($bf,full_tag("PARENT",$level + 2,false,$question->parent)); fwrite ($bf,full_tag("PARENT",$level + 2,false,$question->parent));
@ -152,10 +193,14 @@
fwrite ($bf,full_tag("STAMP",$level + 2,false,$question->stamp)); fwrite ($bf,full_tag("STAMP",$level + 2,false,$question->stamp));
fwrite ($bf,full_tag("VERSION",$level + 2,false,$question->version)); fwrite ($bf,full_tag("VERSION",$level + 2,false,$question->version));
fwrite ($bf,full_tag("HIDDEN",$level + 2,false,$question->hidden)); fwrite ($bf,full_tag("HIDDEN",$level + 2,false,$question->hidden));
fwrite ($bf,full_tag("TIMECREATED",$level + 2,false,$question->timecreated));
fwrite ($bf,full_tag("TIMEMODIFIED",$level + 2,false,$question->timemodified));
fwrite ($bf,full_tag("CREATEDBY",$level + 2,false,$question->createdby));
fwrite ($bf,full_tag("MODIFIEDBY",$level + 2,false,$question->modifiedby));
// Backup question type specific data // Backup question type specific data
$status = $QTYPES[$question->qtype]->backup($bf,$preferences,$question->id, $level + 2); $status = $status && $QTYPES[$question->qtype]->backup($bf,$preferences,$question->id, $level + 2);
//End question //End question
$status = fwrite ($bf,end_tag("QUESTION",$level + 1,true)); $status = $status && fwrite ($bf,end_tag("QUESTION",$level + 1,true));
//Do some output //Do some output
$counter++; $counter++;
if ($counter % 10 == 0) { if ($counter % 10 == 0) {
@ -167,7 +212,7 @@
} }
} }
//Write end tag //Write end tag
$status = fwrite ($bf,end_tag("QUESTIONS",$level,true)); $status = $status && fwrite ($bf,end_tag("QUESTIONS",$level,true));
} }
return $status; return $status;
} }
@ -183,18 +228,18 @@
$answers = get_records("question_answers","question",$question,"id"); $answers = get_records("question_answers","question",$question,"id");
//If there are answers //If there are answers
if ($answers) { if ($answers) {
$status = fwrite ($bf,start_tag("ANSWERS",$level,true)); $status = $status && fwrite ($bf,start_tag("ANSWERS",$level,true));
//Iterate over each answer //Iterate over each answer
foreach ($answers as $answer) { foreach ($answers as $answer) {
$status = fwrite ($bf,start_tag("ANSWER",$level + 1,true)); $status = $status && fwrite ($bf,start_tag("ANSWER",$level + 1,true));
//Print answer contents //Print answer contents
fwrite ($bf,full_tag("ID",$level + 2,false,$answer->id)); fwrite ($bf,full_tag("ID",$level + 2,false,$answer->id));
fwrite ($bf,full_tag("ANSWER_TEXT",$level + 2,false,$answer->answer)); fwrite ($bf,full_tag("ANSWER_TEXT",$level + 2,false,$answer->answer));
fwrite ($bf,full_tag("FRACTION",$level + 2,false,$answer->fraction)); fwrite ($bf,full_tag("FRACTION",$level + 2,false,$answer->fraction));
fwrite ($bf,full_tag("FEEDBACK",$level + 2,false,$answer->feedback)); fwrite ($bf,full_tag("FEEDBACK",$level + 2,false,$answer->feedback));
$status = fwrite ($bf,end_tag("ANSWER",$level + 1,true)); $status = $status && fwrite ($bf,end_tag("ANSWER",$level + 1,true));
} }
$status = fwrite ($bf,end_tag("ANSWERS",$level,true)); $status = $status && fwrite ($bf,end_tag("ANSWERS",$level,true));
} }
return $status; return $status;
} }
@ -209,17 +254,17 @@
$numerical_units = get_records("question_numerical_units","question",$question,"id"); $numerical_units = get_records("question_numerical_units","question",$question,"id");
//If there are numericals_units //If there are numericals_units
if ($numerical_units) { if ($numerical_units) {
$status = fwrite ($bf,start_tag("NUMERICAL_UNITS",$level,true)); $status = $status && fwrite ($bf,start_tag("NUMERICAL_UNITS",$level,true));
//Iterate over each numerical_unit //Iterate over each numerical_unit
foreach ($numerical_units as $numerical_unit) { foreach ($numerical_units as $numerical_unit) {
$status = fwrite ($bf,start_tag("NUMERICAL_UNIT",$level+1,true)); $status = $status && fwrite ($bf,start_tag("NUMERICAL_UNIT",$level+1,true));
//Print numerical_unit contents //Print numerical_unit contents
fwrite ($bf,full_tag("MULTIPLIER",$level+2,false,$numerical_unit->multiplier)); fwrite ($bf,full_tag("MULTIPLIER",$level+2,false,$numerical_unit->multiplier));
fwrite ($bf,full_tag("UNIT",$level+2,false,$numerical_unit->unit)); fwrite ($bf,full_tag("UNIT",$level+2,false,$numerical_unit->unit));
//Now backup numerical_units //Now backup numerical_units
$status = fwrite ($bf,end_tag("NUMERICAL_UNIT",$level+1,true)); $status = $status && fwrite ($bf,end_tag("NUMERICAL_UNIT",$level+1,true));
} }
$status = fwrite ($bf,end_tag("NUMERICAL_UNITS",$level,true)); $status = $status && fwrite ($bf,end_tag("NUMERICAL_UNITS",$level,true));
} }
return $status; return $status;
@ -251,7 +296,7 @@
fwrite ($bf,full_tag("OPTIONS",$level+2,false,$def->options)); fwrite ($bf,full_tag("OPTIONS",$level+2,false,$def->options));
fwrite ($bf,full_tag("ITEMCOUNT",$level+2,false,$def->itemcount)); fwrite ($bf,full_tag("ITEMCOUNT",$level+2,false,$def->itemcount));
//Now backup dataset_entries //Now backup dataset_entries
$status = question_backup_dataset_items($bf,$preferences,$def->id,$level+2); $status = $status && question_backup_dataset_items($bf,$preferences,$def->id,$level+2);
//End dataset definition //End dataset definition
$status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITION",$level+1,true)); $status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITION",$level+1,true));
} }
@ -303,11 +348,11 @@
//If there are states //If there are states
if ($question_states) { if ($question_states) {
//Write start tag //Write start tag
$status = fwrite ($bf,start_tag("STATES",$level,true)); $status = $status && fwrite ($bf,start_tag("STATES",$level,true));
//Iterate over each state //Iterate over each state
foreach ($question_states as $state) { foreach ($question_states as $state) {
//Start state //Start state
$status = fwrite ($bf,start_tag("STATE",$level + 1,true)); $status = $status && fwrite ($bf,start_tag("STATE",$level + 1,true));
//Print state contents //Print state contents
fwrite ($bf,full_tag("ID",$level + 2,false,$state->id)); fwrite ($bf,full_tag("ID",$level + 2,false,$state->id));
fwrite ($bf,full_tag("QUESTION",$level + 2,false,$state->question)); fwrite ($bf,full_tag("QUESTION",$level + 2,false,$state->question));
@ -320,10 +365,10 @@
fwrite ($bf,full_tag("RAW_GRADE",$level + 2,false,$state->raw_grade)); fwrite ($bf,full_tag("RAW_GRADE",$level + 2,false,$state->raw_grade));
fwrite ($bf,full_tag("PENALTY",$level + 2,false,$state->penalty)); fwrite ($bf,full_tag("PENALTY",$level + 2,false,$state->penalty));
//End state //End state
$status = fwrite ($bf,end_tag("STATE",$level + 1,true)); $status = $status && fwrite ($bf,end_tag("STATE",$level + 1,true));
} }
//Write end tag //Write end tag
$status = fwrite ($bf,end_tag("STATES",$level,true)); $status = $status && fwrite ($bf,end_tag("STATES",$level,true));
} }
} }
@ -337,11 +382,11 @@
//If there are sessions //If there are sessions
if ($question_sessions) { if ($question_sessions) {
//Write start tag (the funny name 'newest states' has historical reasons) //Write start tag (the funny name 'newest states' has historical reasons)
$status = fwrite ($bf,start_tag("NEWEST_STATES",$level,true)); $status = $status && fwrite ($bf,start_tag("NEWEST_STATES",$level,true));
//Iterate over each newest_state //Iterate over each newest_state
foreach ($question_sessions as $newest_state) { foreach ($question_sessions as $newest_state) {
//Start newest_state //Start newest_state
$status = fwrite ($bf,start_tag("NEWEST_STATE",$level + 1,true)); $status = $status && fwrite ($bf,start_tag("NEWEST_STATE",$level + 1,true));
//Print newest_state contents //Print newest_state contents
fwrite ($bf,full_tag("ID",$level + 2,false,$newest_state->id)); fwrite ($bf,full_tag("ID",$level + 2,false,$newest_state->id));
fwrite ($bf,full_tag("QUESTIONID",$level + 2,false,$newest_state->questionid)); fwrite ($bf,full_tag("QUESTIONID",$level + 2,false,$newest_state->questionid));
@ -350,10 +395,10 @@
fwrite ($bf,full_tag("SUMPENALTY",$level + 2,false,$newest_state->sumpenalty)); fwrite ($bf,full_tag("SUMPENALTY",$level + 2,false,$newest_state->sumpenalty));
fwrite ($bf,full_tag("MANUALCOMMENT",$level + 2,false,$newest_state->manualcomment)); fwrite ($bf,full_tag("MANUALCOMMENT",$level + 2,false,$newest_state->manualcomment));
//End newest_state //End newest_state
$status = fwrite ($bf,end_tag("NEWEST_STATE",$level + 1,true)); $status = $status && fwrite ($bf,end_tag("NEWEST_STATE",$level + 1,true));
} }
//Write end tag //Write end tag
$status = fwrite ($bf,end_tag("NEWEST_STATES",$level,true)); $status = $status && fwrite ($bf,end_tag("NEWEST_STATES",$level,true));
} }
return $status; return $status;
} }
@ -373,21 +418,44 @@
global $CFG; global $CFG;
return get_records_sql ("SELECT q.id, q.category return get_records_sql ("SELECT old_id, backup_code
FROM {$CFG->prefix}backup_ids a, FROM {$CFG->prefix}backup_ids
{$CFG->prefix}question q WHERE backup_code = '$backup_unique_code' AND
WHERE a.backup_code = '$backup_unique_code' AND table_name = 'question'");
q.category = a.old_id AND
a.table_name = 'question_categories'");
} }
//Delete category ids from backup_ids table //Delete category ids from backup_ids table
function delete_category_ids ($backup_unique_code) { function delete_ids ($backup_unique_code, $tablename) {
global $CFG; global $CFG;
$status = true;
$status = execute_sql("DELETE FROM {$CFG->prefix}backup_ids $status = execute_sql("DELETE FROM {$CFG->prefix}backup_ids
WHERE backup_code = '$backup_unique_code'",false); WHERE backup_code = '$backup_unique_code' AND table_name = '$tablename'",false);
return $status;
}
function question_insert_site_file_names($course, $backup_unique_code){
global $QTYPES, $CFG;
$status = true;
$questionids = question_ids_by_backup ($backup_unique_code);
$urls = array();
if ($questionids){
foreach ($questionids as $question_bk){
$question = get_record('question', 'id', $question_bk->old_id);
$QTYPES[$question->qtype]->get_question_options(&$question);
$urls = array_merge_recursive($urls, $QTYPES[$question->qtype]->find_file_links($question, SITEID));
}
}
ksort($urls);
foreach (array_keys($urls) as $url){
if (file_exists($CFG->dataroot.'/'.SITEID.'/'.$url)){
$inserturl = new object();
$inserturl->backup_code = $backup_unique_code;
$inserturl->file_type = 'site';
$url = clean_param($url, PARAM_PATH);
$inserturl->path = addslashes($url);
$status = $status && insert_record('backup_files', $inserturl);
} else {
notify(get_string('linkedfiledoesntexist', 'question', $url));
}
}
return $status; return $status;
} }
?> ?>

View file

@ -9,13 +9,12 @@
*/ */
require_once("../config.php"); require_once("../config.php");
require_once("editlib.php"); require_once($CFG->dirroot."/question/editlib.php");
require_once("category_class.php"); require_once($CFG->dirroot."/question/category_class.php");
list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup(); list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('categories');
// get values from form for actions on this page // get values from form for actions on this page
$param = new stdClass(); $param = new stdClass();
@ -23,36 +22,64 @@
$param->moveup = optional_param('moveup', 0, PARAM_INT); $param->moveup = optional_param('moveup', 0, PARAM_INT);
$param->movedown = optional_param('movedown', 0, PARAM_INT); $param->movedown = optional_param('movedown', 0, PARAM_INT);
$param->moveupcontext = optional_param('moveupcontext', 0, PARAM_INT);
$param->movedowncontext = optional_param('movedowncontext', 0, PARAM_INT);
$param->tocontext = optional_param('tocontext', 0, PARAM_INT);
$param->left = optional_param('left', 0, PARAM_INT); $param->left = optional_param('left', 0, PARAM_INT);
$param->right = optional_param('right', 0, PARAM_INT); $param->right = optional_param('right', 0, PARAM_INT);
$param->hide = optional_param('hide', 0, PARAM_INT);
$param->delete = optional_param('delete', 0, PARAM_INT); $param->delete = optional_param('delete', 0, PARAM_INT);
$param->confirm = optional_param('confirm', 0, PARAM_INT); $param->confirm = optional_param('confirm', 0, PARAM_INT);
$param->cancel = optional_param('cancel', '', PARAM_ALPHA); $param->cancel = optional_param('cancel', '', PARAM_ALPHA);
$param->move = optional_param('move', 0, PARAM_INT); $param->move = optional_param('move', 0, PARAM_INT);
$param->moveto = optional_param('moveto', 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_NOTAGS);
$param->edit = optional_param('edit', 0, PARAM_INT); $param->edit = optional_param('edit', 0, PARAM_INT);
$param->updateid = optional_param('updateid', 0, PARAM_INT);
if (! $course = get_record("course", "id", $courseid)) { $qcobject = new question_category_object($pagevars['cpage'], $thispageurl, $contexts->having_one_edit_tab_cap('categories'), $param->edit, $pagevars['cat'], $param->delete,
error("Course ID is incorrect"); $contexts->having_cap('moodle/question:add'));
}
$context = get_context_instance(CONTEXT_COURSE, $courseid);
require_capability('moodle/question:managecategory', $context);
$qcobject = new question_category_object($pagevars['cpage'], $thispageurl);
$streditingcategories = get_string('editcategories', 'quiz'); $streditingcategories = get_string('editcategories', 'quiz');
if ($qcobject->editlist->process_actions($param->left, $param->right, $param->moveup, $param->movedown)) { if ($param->left || $param->right || $param->moveup || $param->movedown|| $param->moveupcontext || $param->movedowncontext){
//processing of these actions is handled in the method and page redirects. confirm_sesskey();
} else if ($cm!==null) { foreach ($qcobject->editlists as $list){
//processing of these actions is handled in the method where appropriate and page redirects.
$list->process_actions($param->left, $param->right, $param->moveup, $param->movedown,
$param->moveupcontext, $param->movedowncontext, $param->tocontext);
}
}
if ($param->delete && ($questionstomove = count_records("question", "category", $param->delete))){
if (!$category = get_record("question_categories", "id", $param->delete)) { // security
error("No such category {$param->delete}!", $thispageurl->out());
}
$categorycontext = get_context_instance_by_id($category->contextid);
$qcobject->moveform = new question_move_form($thispageurl,
array('contexts'=>array($categorycontext), 'currentcat'=>$param->delete));
if ($qcobject->moveform->is_cancelled()){
redirect($thispageurl->out());
} elseif ($formdata = $qcobject->moveform->get_data()) {
/// 'confirm' is the category to move existing questions to
$qcobject->move_questions_and_delete_category($formdata->delete, $formdata->category);
redirect($thispageurl->out());
}
} else {
$questionstomove = 0;
}
if ($qcobject->catform->is_cancelled()){
redirect($thispageurl->out());
}elseif ($catformdata = $qcobject->catform->get_data()) {
if (!$catformdata->id) {//new category
$qcobject->add_category($catformdata->parent, $catformdata->name, $catformdata->info);
} else {
$qcobject->update_category($catformdata->id, $catformdata->parent, $catformdata->name, $catformdata->info);
}
redirect($thispageurl->out());
} elseif ((!empty($param->delete) and (!$questionstomove) and confirm_sesskey())) {
$qcobject->delete_category($param->delete);//delete the category now no questions to move
}
$crumbs = array();
if ($cm!==null) {
// Page header // Page header
$strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id)) $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', $cm->modname)) ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: ""; : "";
$navlinks = array(); $navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname),
@ -61,62 +88,42 @@
$navlinks[] = array('name' => format_string($module->name), $navlinks[] = array('name' => format_string($module->name),
'link' => "$CFG->wwwroot/mod/{$cm->modname}/view.php?cmid={$cm->id}", 'link' => "$CFG->wwwroot/mod/{$cm->modname}/view.php?cmid={$cm->id}",
'type' => 'title'); 'type' => 'title');
} else {
// Print basic page layout.
$strupdatemodule = '';
$navlinks = array();
}
if (!$param->edit){
$navlinks[] = array('name' => $streditingcategories, 'link' => '', 'type' => 'title'); $navlinks[] = array('name' => $streditingcategories, 'link' => '', 'type' => 'title');
} else {
$navlinks[] = array('name' => $streditingcategories, 'link' => $thispageurl->out(), 'type' => 'title');
$navlinks[] = array('name' => get_string('editingcategory', 'question'), 'link' => '', 'type' => 'title');
}
$navigation = build_navigation($navlinks); $navigation = build_navigation($navlinks);
print_header_simple($streditingcategories, '', $navigation, "", "", true, $strupdatemodule); print_header_simple($streditingcategories, '', $navigation, "", "", true, $strupdatemodule);
// print tabs
if ($cm!==null) {
$currenttab = 'edit'; $currenttab = 'edit';
$mode = 'categories'; $mode = 'categories';
${$cm->modname} = $module; ${$cm->modname} = $module;
include($CFG->dirroot."/mod/{$cm->modname}/tabs.php"); include($CFG->dirroot."/mod/{$cm->modname}/tabs.php");
} else { } else {
// Print basic page layout.
$navlinks = array();
$navlinks[] = array('name' => $streditingcategories, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
print_header_simple($streditingcategories, '', $navigation);
// print tabs
$currenttab = 'categories'; $currenttab = 'categories';
$context = $contexts->lowest();
include('tabs.php'); include('tabs.php');
} }
// Process actions. // display UI
if (isset($_REQUEST['sesskey']) and confirm_sesskey()) { // sesskey must be ok if (!empty($param->edit)) {
if (!empty($param->delete) and empty($param->cancel)) { $qcobject->edit_single_category($param->edit);
if (!empty($param->confirm)) { } else if ($questionstomove){
/// 'confirm' is the category to move existing questions to $qcobject->display_move_form($questionstomove, $category);
$qcobject->delete_category($param->delete, $param->confirm);
} else { } else {
$qcobject->delete_category($param->delete);
}
} else if (!empty($param->hide)) {
$qcobject->publish_category(false, $param->hide);
} 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_NOTAGS);
$param->newinfo = required_param('newinfo',PARAM_NOTAGS);
$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, $pagevars['cpage']);
} else if (!empty($param->updateid)) {
$param->updateparent = required_param('updateparent',PARAM_INT);
$param->updatename = required_param('updatename',PARAM_NOTAGS);
$param->updateinfo = required_param('updateinfo',PARAM_NOTAGS);
$param->updatepublish = required_param('updatepublish',PARAM_INT);
$qcobject->update_category($param->updateid, $param->updateparent, $param->updatename,
$param->updateinfo, $param->updatepublish, $course->id);
}
}
// display the user interface // display the user interface
$qcobject->display_user_interface(); $qcobject->display_user_interface();
}
print_footer($course); print_footer($COURSE);
?> ?>

View file

@ -8,64 +8,91 @@
*/ */
// number of categories to display on page // number of categories to display on page
define("QUESTION_PAGE_LENGTH", 20); define("QUESTION_PAGE_LENGTH", 25);
require_once("$CFG->libdir/listlib.php"); require_once("$CFG->libdir/listlib.php");
require_once("$CFG->dirroot/question/category_form.php");
require_once('move_form.php');
class question_category_list extends moodle_list { class question_category_list extends moodle_list {
var $table = "question_categories"; var $table = "question_categories";
var $listitemclassname = 'question_category_list_item'; var $listitemclassname = 'question_category_list_item';
/**
* @var reference to list displayed below this one.
*/
var $nextlist = null;
/**
* @var reference to list displayed above this one.
*/
var $lastlist = null;
var $context = null;
function question_category_list($type='ul', $attributes='', $editable = false, $pageurl=null, $page = 0, $pageparamname = 'page', $itemsperpage = 20, $context = null){
parent::moodle_list('ul', '', $editable, $pageurl, $page, 'cpage', $itemsperpage);
$this->context = $context;
}
function get_records() { function get_records() {
global $COURSE, $CFG; $this->records = get_categories_for_contexts($this->context->id, $this->sortby);
$categories = get_records($this->table, 'course', "{$COURSE->id}", $this->sortby);
$catids = array_keys($categories);
$select = "WHERE category IN ('".join("', '", $catids)."') AND hidden='0' AND parent='0'";
$questioncounts = get_records_sql_menu('SELECT category, COUNT(*) FROM '. $CFG->prefix . 'question' .' '. $select.' GROUP BY category');
foreach ($categories as $categoryid => $category){
if (isset($questioncounts[$categoryid])){
$categories[$categoryid]->questioncount = $questioncounts[$categoryid];
} else {
$categories[$categoryid]->questioncount = 0;
} }
function process_actions($left, $right, $moveup, $movedown, $moveupcontext, $movedowncontext, $tocontext){
global $CFG;
//parent::procces_actions redirects after any action
parent::process_actions($left, $right, $moveup, $movedown);
if ($tocontext == $this->context->id){
//only called on toplevel list
if ($moveupcontext){
$cattomove = $moveupcontext;
$totop = 0;
} elseif ($movedowncontext){
$cattomove = $movedowncontext;
$totop = 1;
}
$toparent = "0,{$this->context->id}";
redirect($CFG->wwwroot.'/question/contextmove.php?'.
$this->pageurl->get_query_string(compact('cattomove', 'totop', 'toparent')));
} }
$this->records = $categories;
} }
} }
class question_category_list_item extends list_item { class question_category_list_item extends list_item {
function set_icon_html($first, $last, &$lastitem){
global $CFG;
$category = $this->item;
$this->icons['edit']= $this->image_icon(get_string('editthiscategory'),
"{$CFG->wwwroot}/question/category.php?".$this->parentlist->pageurl->get_query_string(array('edit'=>$category->id)), 'edit');
parent::set_icon_html($first, $last, $lastitem);
$toplevel = ($this->parentlist->parentitem === null);//this is a top level item
if (($this->parentlist->nextlist !== null) && $last && $toplevel && (count($this->parentlist->items)>1)){
$this->icons['down'] = $this->image_icon(get_string('shareincontext', 'question', print_context_name($this->parentlist->nextlist->context)),
$this->parentlist->pageurl->out_action(array('movedowncontext'=>$this->id, 'tocontext'=>$this->parentlist->nextlist->context->id)), 'down');
}
if (($this->parentlist->lastlist !== null) && $first && $toplevel && (count($this->parentlist->items)>1)){
$this->icons['up'] = $this->image_icon(get_string('shareincontext', 'question', print_context_name($this->parentlist->lastlist->context)),
$this->parentlist->pageurl->out_action(array('moveupcontext'=>$this->id, 'tocontext'=>$this->parentlist->lastlist->context->id)), 'up');
}
}
function item_html($extraargs = array()){ function item_html($extraargs = array()){
global $CFG; global $CFG;
$pixpath = $CFG->pixpath; $pixpath = $CFG->pixpath;
$str = $extraargs['str']; $str = $extraargs['str'];
$category = $this->item; $category = $this->item;
$linkcss = $category->publish ? ' class="published" ' : ' class="unpublished" '; $editqestions = get_string('editquestions', 'quiz');
/// Each section adds html to be displayed as part of this list item /// Each section adds html to be displayed as part of this list item
$questionbankurl = "{$CFG->wwwroot}/question/edit.php?".
$this->parentlist->pageurl->get_query_string(array('category'=>"$category->id,$category->contextid"));
$item = '<a ' . $linkcss . ' title="' . $str->edit. '" href="'.$this->parentlist->pageurl->out_action(array('edit'=>$this->id)).'"> $catediturl = $this->parentlist->pageurl->out(false, array('edit'=>$this->id));
<img src="' . $pixpath . '/t/edit.gif" class="iconsmall" $item = "<a title=\"{$str->edit}\" href=\"$catediturl\">".$category->name ."</a> <a title=\"$editqestions\" href=\"$questionbankurl\">".'('.$category->questioncount.')</a>';
alt="' .$str->edit. '" /> ' . $category->name . '('.$category->questioncount.')'. '</a>';
$item .= '&nbsp;'. $category->info; $item .= '&nbsp;'. $category->info;
if (!empty($category->publish)) { if (count($this->parentlist->records)!=1){ // don't allow delete if this is the last category in this context.
$item .= '<a title="' . $str->hide . '" href="'.$this->parentlist->pageurl->out_action(array('hide'=>$this->id)).'">
<img src="' . $pixpath . '/t/hide.gif" class="iconsmall" alt="' .$str->hide. '" /></a> ';
} else {
$item .= '<a title="' . $str->publish . '" href="'.$this->parentlist->pageurl->out_action(array('publish'=>$this->id)).'">
<img src="' . $pixpath . '/t/show.gif" class="iconsmall" alt="' .$str->publish. '" /></a> ';
}
if ($category->id != $extraargs['defaultcategory']->id) {
$item .= '<a title="' . $str->delete . '" href="'.$this->parentlist->pageurl->out_action(array('delete'=>$this->id)).'"> $item .= '<a title="' . $str->delete . '" href="'.$this->parentlist->pageurl->out_action(array('delete'=>$this->id)).'">
<img src="' . $pixpath . '/t/delete.gif" class="iconsmall" alt="' .$str->delete. '" /></a> '; <img src="' . $pixpath . '/t/delete.gif" class="iconsmall" alt="' .$str->delete. '" /></a> ';
} }
@ -88,29 +115,30 @@ class question_category_object {
var $str; var $str;
var $pixpath; var $pixpath;
/** /**
* Nested list to display categories. * Nested lists to display categories.
* *
* @var question_category_list * @var array
*/ */
var $editlist; var $editlists = array();
var $newtable; var $newtable;
var $tab; var $tab;
var $tabsize = 3; var $tabsize = 3;
var $categories;
var $categorystrings;
var $defaultcategory;
//------------------------------------------------------ //------------------------------------------------------
/** /**
* @var moodle_url Object representing url for this page * @var moodle_url Object representing url for this page
*/ */
var $pageurl; var $pageurl;
/**
* @var question_category_edit_form Object representing form for adding / editing categories.
*/
var $catform;
/** /**
* Constructor * Constructor
* *
* Gets necessary strings and sets relevant path information * Gets necessary strings and sets relevant path information
*/ */
function question_category_object($page, $pageurl) { function question_category_object($page, $pageurl, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts) {
global $CFG, $COURSE; global $CFG, $COURSE;
$this->tab = str_repeat('&nbsp;', $this->tabsize); $this->tab = str_repeat('&nbsp;', $this->tabsize);
@ -138,14 +166,38 @@ class question_category_object {
$this->str->page = get_string('page'); $this->str->page = get_string('page');
$this->pixpath = $CFG->pixpath; $this->pixpath = $CFG->pixpath;
$this->editlist = new question_category_list('ul', '', true, $pageurl, $page, 'cpage', QUESTION_PAGE_LENGTH);
$this->pageurl = $pageurl; $this->pageurl = $pageurl;
$this->initialize(); $this->initialize($page, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts);
} }
/**
* Initializes this classes general category-related variables
*/
function initialize($page, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts) {
$lastlist = null;
foreach ($contexts as $context){
$this->editlists[$context->id] = new question_category_list('ul', '', true, $this->pageurl, $page, 'cpage', QUESTION_PAGE_LENGTH, $context);
$this->editlists[$context->id]->lastlist =& $lastlist;
if ($lastlist!== null){
$lastlist->nextlist =& $this->editlists[$context->id];
}
$lastlist =& $this->editlists[$context->id];
}
$count = 1;
$paged = false;
foreach ($this->editlists as $key => $list){
list($paged, $count) = $list->list_from_records($paged, $count);
}
$this->catform = new question_category_edit_form($this->pageurl, compact('contexts', 'currentcat'));
if (!$currentcat){
$this->catform->set_data(array('parent'=>$defaultcategory));
}
}
/** /**
* Displays the user interface * Displays the user interface
* *
@ -153,91 +205,21 @@ class question_category_object {
function display_user_interface() { function display_user_interface() {
/// Interface for editing existing categories /// Interface for editing existing categories
print_heading_with_help($this->str->editcategories, 'categories', 'quiz'); $this->output_edit_lists();
$this->output_edit_list();
echo '<br />'; echo '<br />';
/// Interface for adding a new category: /// Interface for adding a new category:
print_heading_with_help($this->str->addcategory, 'categories_edit', 'quiz');
$this->output_new_table(); $this->output_new_table();
echo '<br />'; echo '<br />';
} }
/**
* Initializes this classes general category-related variables
*/
function initialize() {
global $COURSE, $CFG;
/// Get the existing categories
if (!$this->defaultcategory = get_default_question_category($COURSE->id)) {
error("Error: Could not find or make a category!");
}
$this->editlist->list_from_records();
$this->categories = $this->editlist->records;
// create the array of id=>full_name strings
$this->categorystrings = $this->expanded_category_strings($this->categories);
}
/** /**
* Outputs a table to allow entry of a new category * Outputs a table to allow entry of a new category
*/ */
function output_new_table() { function output_new_table() {
global $USER, $COURSE; $this->catform->display();
$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'] = '<input type="text" name="newcategory" value="" size="15" />';
$this->newtable->align['info'] = "left";
$this->newtable->wrap['info'] = "nowrap";
$row['info'] = '<input type="text" name="newinfo" value="" size="50" />';
$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'] = '<input type="submit" value="' . $this->str->add . '" />';
$this->newtable->data[] = $row;
// wrap the table in a form and output it
echo '<form action="category.php" method="post">';
echo '<fieldset class="invisiblefieldset" style="display: block">';
echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
echo $this->pageurl->hidden_params_out();
echo '<input type="hidden" name="addcategory" value="true" />';
print_table($this->newtable);
echo '</fieldset>';
echo '</form>';
} }
@ -247,13 +229,18 @@ class question_category_object {
* $this->initialize() must have already been called * $this->initialize() must have already been called
* *
*/ */
function output_edit_list() { function output_edit_lists() {
print_heading_with_help(get_string('editcategories', 'quiz'), 'categories', 'quiz');
foreach ($this->editlists as $context => $list){
$listhtml = $list->to_html(0, array('str'=>$this->str));
if ($listhtml){
print_heading(get_string('questioncatsfor', 'question', print_context_name(get_context_instance_by_id($context))), '', 3);
print_box_start('boxwidthwide boxaligncenter generalbox'); print_box_start('boxwidthwide boxaligncenter generalbox');
echo $this->editlist->to_html(0, array('str'=>$this->str, echo $listhtml;
'defaultcategory' => $this->defaultcategory));
print_box_end(); print_box_end();
echo $this->editlist->display_page_numbers(); }
}
echo $list->display_page_numbers();
} }
@ -279,103 +266,20 @@ class question_category_object {
function edit_single_category($categoryid) { function edit_single_category($categoryid) {
/// Interface for adding a new category /// Interface for adding a new category
global $CFG, $USER, $COURSE; global $COURSE;
/// Interface for editing existing categories /// Interface for editing existing categories
if ($category = get_record("question_categories", "id", $categoryid)) { if ($category = get_record("question_categories", "id", $categoryid)) {
print_heading_with_help($this->str->edit, 'categories_edit', 'quiz');
$this->output_edit_single_table($category); $category->parent = "$category->parent,$category->contextid";
echo '<div class="centerpara">'; $category->submitbutton = get_string('savechanges');
print_single_button($CFG->wwwroot . '/question/category.php', $category->categoryheader = $this->str->edit;
$this->pageurl->params, $this->str->cancel); $this->catform->set_data($category);
echo '</div>'; $this->catform->display();
print_footer($COURSE);
exit;
} else { } else {
error("Category $categoryid not found", "category.php?id={$COURSE->id}"); error("Category $categoryid not found");
} }
} }
/**
* Outputs a table to allow editing of an existing category
*
* @param object category
* @param int page current page
*/
function output_edit_single_table($category) {
global $USER;
$publishoptions[0] = get_string("no");
$publishoptions[1] = get_string("yes");
$strupdate = get_string('update');
$edittable = new stdClass;
$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'] = '<input type="text" name="updatename" value="' . format_string($category->name) . '" size="15" />';
$edittable->align['info'] = "left";
$edittable->wrap['info'] = "nowrap";
$row['info'] = '<input type="text" name="updateinfo" value="' . $category->info . '" size="50" />';
$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'] = '<input type="submit" value="' . $strupdate . '" />';
$edittable->data[] = $row;
// wrap the table in a form and output it
echo '<p><form action="category.php" method="post">';
echo '<fieldset class="invisiblefieldset" style="display: block;">';
echo '<input type="hidden" name="sesskey" value="' . $USER->sesskey . '" />';
echo $this->pageurl->hidden_params_out();
echo '<input type="hidden" name="updateid" value="' . $category->id . '" />';
print_table($edittable);
echo '</fieldset>';
echo '</form></p>';
}
/**
* 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;
}
/** /**
* Sets the viable parents * Sets the viable parents
@ -418,55 +322,16 @@ class question_category_object {
* Deletes an existing question category * Deletes an existing question category
* *
* @param int deletecat id of category to delete * @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) { function delete_category($categoryid) {
global $USER, $COURSE; global $CFG;
question_can_delete_cat($categoryid);
if (!$category = get_record("question_categories", "id", $deletecat)) { // security if (!$category = get_record("question_categories", "id", $categoryid)) { // security
error("No such category $deletecat!", "category.php?id={$COURSE->id}"); error("No such category $cat!", $this->pageurl->out());
} }
if (!is_null($destcategoryid)) { // Need to move some questions before deleting the category
if (!$category2 = get_record("question_categories", "id", $destcategoryid)) { // security
error("No such category $destcategoryid!", "category.php?id={$COURSE->id}");
}
if (! set_field('question', 'category', $destcategoryid, 'category', $deletecat)) {
error("Error while moving questions from category '" . format_string($category->name) . "' to '$category2->name'", "category.php?id={$COURSE->id}");
}
} else {
// todo: delete any hidden questions that are not actually in use any more
if ($count = count_records("question", "category", $category->id)) {
$vars = new stdClass;
$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 "<p><div align=\"center\"><form action=\"category.php\" method=\"get\">";
echo '<fieldset class="invisiblefieldset">';
echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
echo "<input type=\"hidden\" name=\"id\" value=\"{$COURSE->id}\" />";
echo "<input type=\"hidden\" name=\"delete\" value=\"$category->id\" />";
choose_from_menu($categorystrings, "confirm", "", "");
echo "<input type=\"submit\" value=\"". get_string("categorymoveto", "quiz") . "\" />";
echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->str->cancel}\" />";
echo '</fieldset>';
echo "</form></div></p>";
print_footer($COURSE);
exit;
}
}
/// Send the children categories to live with their grandparent /// Send the children categories to live with their grandparent
if ($childcats = get_records("question_categories", "parent", $category->id)) { if (!set_field("question_categories", "parent", $category->parent, "parent", $category->id)) {
foreach ($childcats as $childcat) { error("Could not update a child category!", $this->pageurl->out());
if (! set_field("question_categories", "parent", $category->parent, "id", $childcat->id)) {
error("Could not update a child category!", "category.php?id={$COURSE->id}");
}
}
} }
/// Finally delete the category itself /// Finally delete the category itself
@ -475,64 +340,54 @@ class question_category_object {
redirect($this->pageurl->out());//always redirect after successful action redirect($this->pageurl->out());//always redirect after successful action
} }
} }
function move_questions_and_delete_category($oldcat, $newcat){
question_can_delete_cat($oldcat);
$this->move_questions($oldcat, $newcat);
/** $this->delete_category($oldcat);
* 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("question_categories", "id", $categoryid);
if ($tempcat) {
if (! set_field("question_categories", "publish", $publish, "id", $tempcat->id)) {
notify("Could not update that category!");
} else {
redirect($this->pageurl->out());//always redirect after successful action
} }
function display_move_form($questionsincategory, $category){
$vars = new stdClass;
$vars->name = $category->name;
$vars->count = $questionsincategory;
print_simple_box(get_string("categorymove", "quiz", $vars), "center");
$this->moveform->display();
}
function move_questions($oldcat, $newcat){
if (!set_field('question', 'category', $newcat, 'category', $oldcat)) {
error("Error while moving questions from category '$oldcat' to '$newcat'", $this->pageurl->out());
} }
} }
/** /**
* Creates a new category with given params * 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) { function add_category($newparent, $newcategory, $newinfo) {
if (empty($newcategory)) { if (empty($newcategory)) {
notify(get_string('categorynamecantbeblank', 'quiz'), 'notifyproblem'); error(get_string('categorynamecantbeblank', 'quiz'));
return false;
} }
list($parentid, $contextid) = explode(',', $newparent);
//moodle_form makes sure select element output is legal no need for further cleaning
require_capability('moodle/question:managecategory', get_context_instance_by_id($contextid));
if ($newparent) { if ($parentid) {
// first check that the parent category is in the correct course if(!(get_field('question_categories', 'contextid', 'id', $parentid) == $contextid)) {
if(!(get_field('question_categories', 'course', 'id', $newparent) == $newcourse)) { error("Could not insert the new question category '$newcategory' illegal contextid '$contextid'.");
return false;
} }
} }
$cat = NULL; $cat = new object();
$cat->parent = $newparent; $cat->parent = $parentid;
$cat->contextid = $contextid;
$cat->name = $newcategory; $cat->name = $newcategory;
$cat->info = $newinfo; $cat->info = $newinfo;
$cat->publish = $newpublish;
$cat->course = $newcourse;
$cat->sortorder = 999; $cat->sortorder = 999;
$cat->stamp = make_unique_id_code(); $cat->stamp = make_unique_id_code();
if (!insert_record("question_categories", $cat)) { if (!insert_record("question_categories", $cat)) {
error("Could not insert the new question category '$newcategory'", "category.php?id={$newcourse}"); error("Could not insert the new question category '$newcategory'");
} else { } else {
notify(get_string("categoryadded", "quiz", $newcategory), 'notifysuccess');
redirect($this->pageurl->out());//always redirect after successful action redirect($this->pageurl->out());//always redirect after successful action
} }
} }
@ -540,32 +395,75 @@ class question_category_object {
/** /**
* Updates an existing category with given params * 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) { function update_category($updateid, $newparent, $newname, $newinfo) {
if (empty($updatename)) { global $CFG, $QTYPES;
notify(get_string('categorynamecantbeblank', 'quiz'), 'notifyproblem'); if (empty($newname)) {
return false; error(get_string('categorynamecantbeblank', 'quiz'));
} }
list($parentid, $tocontextid) = explode(',', $newparent);
$oldcat = get_record('question_categories', 'id', $updateid);
$fromcontext = get_context_instance_by_id($oldcat->contextid);
require_capability('moodle/question:managecategory', $fromcontext);
if ($oldcat->contextid == $tocontextid){
$tocontext = get_context_instance_by_id($tocontextid);
require_capability('moodle/question:managecategory', $tocontext);
}
$cat = NULL; $cat = NULL;
$cat->id = $updateid; $cat->id = $updateid;
$cat->parent = $updateparent; $cat->name = $newname;
$cat->name = $updatename; $cat->info = $newinfo;
$cat->info = $updateinfo; //never move category where it is the default
$cat->publish = $updatepublish; if (1 != count_records_sql("SELECT count(*) FROM {$CFG->prefix}question_categories as c1, {$CFG->prefix}question_categories as c2 WHERE c2.id = $updateid AND c1.contextid = c2.contextid")){
if ($oldcat->contextid == $tocontextid){ // not moving contexts
$cat->parent = $parentid;
if (!update_record("question_categories", $cat)) { if (!update_record("question_categories", $cat)) {
error("Could not update the category '$updatename'", "category.php?id={$courseid}"); error("Could not update the category '$newname'", $this->pageurl->out());
} else { } else {
notify(get_string("categoryupdated", 'quiz'), 'notifysuccess');
redirect($this->pageurl->out()); redirect($this->pageurl->out());
} }
} else {
if (!update_record("question_categories", $cat)) {
error("Could not update the category '$newname'", $this->pageurl->out());
} else {
redirect($CFG->wwwroot.'/question/contextmove.php?'.
$this->pageurl->get_query_string(array('cattomove' => $updateid,
'toparent'=>$newparent)));
} }
}
} else {
error("Cannot move the category '$newname'. It is the last in this context.", $this->pageurl->out());
}
}
function move_question_from_cat_confirm($fromcat, $fromcourse, $tocat=null, $question=null){
global $QTYPES;
if (!$question){
$questions[] = $question;
} else {
$questions = get_records('question', 'category', $tocat->id);
}
$urls = array();
foreach ($questions as $question){
$urls = array_merge($urls, $QTYPES[$question->qtype]->find_file_links_in_question($question));
}
if ($fromcourse){
$append = 'tocourse';
} else {
$append = 'tosite';
}
if ($tocat){
echo '<p>'.get_string('needtomovethesefilesincat','question').'</p>';
} else {
echo '<p>'.get_string('needtomovethesefilesinquestion','question').'</p>';
}
}
} }
?> ?>

View file

@ -14,18 +14,15 @@
require_once("../config.php"); require_once("../config.php");
require_once("editlib.php"); require_once("editlib.php");
list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup(); list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('questions');
if (! $course = get_record("course", "id", $courseid)) {
error("This course doesn't exist");
}
$context = get_context_instance(CONTEXT_COURSE, $courseid);
question_showbank_actions($thispageurl, $cm);
$context = $contexts->lowest();
$streditingquestions = get_string('editquestions', "quiz"); $streditingquestions = get_string('editquestions', "quiz");
if ($cm!==null) { if ($cm!==null) {
$strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id)) $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', $cm->modname)) ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: ""; : "";
$navlinks = array(); $navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity'); $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity');
@ -55,11 +52,11 @@
echo '<table class="boxaligncenter" border="0" cellpadding="2" cellspacing="0">'; echo '<table class="boxaligncenter" border="0" cellpadding="2" cellspacing="0">';
echo '<tr><td valign="top">'; echo '<tr><td valign="top">';
question_showbank($thispageurl, $cm, $pagevars['qpage'], $pagevars['qperpage'], $pagevars['qsortorder'], $pagevars['qsortorderdecoded'], question_showbank('questions', $contexts, $thispageurl, $cm, $pagevars['qpage'], $pagevars['qperpage'], $pagevars['qsortorder'], $pagevars['qsortorderdecoded'],
$pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['showquestiontext']); $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['showquestiontext']);
echo '</td></tr>'; echo '</td></tr>';
echo '</table>'; echo '</table>';
print_footer($course); print_footer($COURSE);
?> ?>

View file

@ -2,8 +2,6 @@
/** /**
* Functions used to show question editing interface * Functions used to show question editing interface
* *
* TODO: currently the function question_list still provides controls specific
* to the quiz module. This needs to be generalised.
* *
* @author Martin Dougiamas and many others. This has recently been extensively * @author Martin Dougiamas and many others. This has recently been extensively
* rewritten by members of the Serving Mathematics project * rewritten by members of the Serving Mathematics project
@ -78,53 +76,70 @@ function get_questions_category( $category, $noparent=false, $recurse=true, $exp
} }
/** /**
* Gets the default category in a course * Gets the default category in the most specific context.
* If no categories exist yet then default ones are created in all contexts.
* *
* It returns the first category with no parent category. If no categories * @param array $contexts The context objects for this context and all parent contexts.
* exist yet then one is created. * @return object The default category - the category in the course context
* @return object The default category
* @param integer $courseid The id of the course whose default category is wanted
*/ */
function get_default_question_category($courseid) { function question_make_default_categories($contexts) {
// If it already exists, just return it. // If it already exists, just return it.
if ($category = get_records_select("question_categories", "course = '$courseid' AND parent = '0'", 'id', '*', '', 1)) { foreach ($contexts as $key => $context) {
return reset($category); if (!$categoryrs = get_recordset_select("question_categories", "contextid = '{$context->id}'", 'sortorder, name', '*', '', 1)) {
} error('error getting category record');
} else {
if (!$category = rs_fetch_record($categoryrs)){
// Otherwise, we need to make one // Otherwise, we need to make one
$category = new stdClass; $category = new stdClass;
$category->name = get_string("default", "quiz"); $contextname = print_context_name($context, false, true);
$category->info = get_string("defaultinfo", "quiz"); $category->name = addslashes(get_string('defaultfor', 'question', $contextname));
$category->course = $courseid; $category->info = addslashes(get_string('defaultinfofor', 'question', $contextname));
$category->contextid = $context->id;
$category->parent = 0; $category->parent = 0;
$category->sortorder = 999; // By default, all categories get this number, and are sorted alphabetically. $category->sortorder = 999; // By default, all categories get this number, and are sorted alphabetically.
$category->publish = 0;
$category->stamp = make_unique_id_code(); $category->stamp = make_unique_id_code();
if (!$category->id = insert_record('question_categories', $category)) {
if (!$category->id = insert_record("question_categories", $category)) { error('Error creating a default category for context '.print_context_name($context));
notify("Error creating a default category!");
return false;
} }
return $category; }
}
if ($context->contextlevel == CONTEXT_COURSE){
$toreturn = clone($category);
}
}
return $toreturn;
} }
function question_can_delete_cat($todelete){
global $CFG;
$record = get_record_sql("SELECT count(*) as count, c1.contextid as contextid FROM {$CFG->prefix}question_categories as c1,
{$CFG->prefix}question_categories as c2 WHERE c2.id = $todelete
AND c1.contextid = c2.contextid GROUP BY c1.contextid");
$contextid = $record->contextid;
$count = $record->count;
if ($count < 2) {
error('You can\'t delete that category it is the default category for this context.');
} else {
require_capability('moodle/question:managecategory', get_context_instance_by_id($contextid));
}
}
/** /**
* prints a form to choose categories * prints a form to choose categories
*/ */
function question_category_form($course, $pageurl, $current, $recurse=1, $showhidden=false, $showquestiontext=false) { function question_category_form($contexts, $pageurl, $current, $recurse=1, $showhidden=false, $showquestiontext=false) {
global $CFG; global $CFG;
/// Make sure the default category exists for this course
get_default_question_category($course->id);
/// Get all the existing categories now /// Get all the existing categories now
$catmenu = question_category_options($course->id, true); $catmenu = question_category_options($contexts, false, 0, true);
$strcategory = get_string("category", "quiz"); $strcategory = get_string('category', 'quiz');
$strshow = get_string("show", "quiz"); $strshow = get_string('show', 'quiz');
$streditcats = get_string("editcategories", "quiz"); $streditcats = get_string('editcategories', 'quiz');
popup_form ("edit.php?".$pageurl->get_query_string()."&amp;category=", $catmenu, "catmenu", $current, "", "", "", false, "self", "<strong>$strcategory</strong>"); popup_form ('edit.php?'.$pageurl->get_query_string().'&amp;category=', $catmenu, 'catmenu', $current, '', '', '', false, 'self', "<strong>$strcategory</strong>");
echo '<form method="get" action="edit.php" id="displayoptions">'; echo '<form method="get" action="edit.php" id="displayoptions">';
echo "<fieldset class='invisiblefieldset'>"; echo "<fieldset class='invisiblefieldset'>";
@ -163,11 +178,15 @@ function question_category_form_checkbox($name, $checked) {
* @param boolean $showhidden True if also hidden questions should be displayed * @param boolean $showhidden True if also hidden questions should be displayed
* @param boolean $showquestiontext whether the text of each question should be shown in the list * @param boolean $showquestiontext whether the text of each question should be shown in the list
*/ */
function question_list($course, $pageurl, $categoryid, $cm = null, function question_list($contexts, $pageurl, $categoryandcontext, $cm = null,
$recurse=1, $page=0, $perpage=100, $showhidden=false, $sortorder='typename', $sortorderdecoded='qtype, name ASC', $recurse=1, $page=0, $perpage=100, $showhidden=false, $sortorder='typename', $sortorderdecoded='qtype, name ASC',
$showquestiontext = false) { $showquestiontext = false, $addcontexts = array()) {
global $QTYPE_MENU, $USER, $CFG, $THEME, $COURSE;
list($categoryid, $contextid)= explode(',', $categoryandcontext);
$qtypemenu = $QTYPE_MENU;
global $QTYPE_MENU, $USER, $CFG, $THEME;
$strcategory = get_string("category", "quiz"); $strcategory = get_string("category", "quiz");
$strquestion = get_string("question", "quiz"); $strquestion = get_string("question", "quiz");
@ -182,6 +201,8 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
$strquestionname = get_string("questionname", "quiz"); $strquestionname = get_string("questionname", "quiz");
$strdelete = get_string("delete"); $strdelete = get_string("delete");
$stredit = get_string("edit"); $stredit = get_string("edit");
$strmove = get_string('moveqtoanothercontext', 'question');
$strview = get_string("view");
$straction = get_string("action"); $straction = get_string("action");
$strrestore = get_string('restore'); $strrestore = get_string('restore');
@ -196,43 +217,54 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
return; return;
} }
if (!$category = get_record('question_categories', 'id', $categoryid)) { if (!$category = get_record('question_categories', 'id', $categoryid, 'contextid', $contextid)) {
notify('Category not found!'); notify('Category not found!');
return; return;
} }
$canedit = has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $category->course)); $catcontext = get_context_instance_by_id($contextid);
$canadd = has_capability('moodle/question:add', $catcontext);
//check for capabilities on all questions in category, will also apply to sub cats.
$caneditall =has_capability('moodle/question:editall', $catcontext);
$canuseall =has_capability('moodle/question:useall', $catcontext);
$canmoveall =has_capability('moodle/question:moveall', $catcontext);
if ($cm AND $cm->modname == 'quiz') { if ($cm AND $cm->modname == 'quiz') {
$quizid = $cm->instance; $quizid = $cm->instance;
} else { } else {
$quizid = 0; $quizid = 0;
} }
$returnurl = $pageurl->out();
$questionurl = new moodle_url("$CFG->wwwroot/question/question.php",
array('returnurl' => $returnurl));
if ($cm!==null){
$questionurl->param('cmid', $cm->id);
} else {
$questionurl->param('courseid', $COURSE->id);
}
$questionmoveurl = new moodle_url("$CFG->wwwroot/question/contextmoveq.php",
array('returnurl' => $returnurl));
if ($cm!==null){
$questionmoveurl->param('cmid', $cm->id);
} else {
$questionmoveurl->param('courseid', $COURSE->id);
}
echo '<div class="boxaligncenter">'; echo '<div class="boxaligncenter">';
$formatoptions = new stdClass; $formatoptions = new stdClass;
$formatoptions->noclean = true; $formatoptions->noclean = true;
echo format_text($category->info, FORMAT_MOODLE, $formatoptions, $course->id); echo format_text($category->info, FORMAT_MOODLE, $formatoptions, $COURSE->id);
echo '<table><tr>'; echo '<table><tr>';
// check if editing questions in this category is allowed if ($canadd) {
if ($canedit) {
echo '<td valign="top" align="right">'; echo '<td valign="top" align="right">';
$returnurl = urlencode($pageurl->out()); popup_form ($questionurl->out(false, array('category' => $category->id)).'&amp;qtype=', $qtypemenu, "addquestion", "", "choose", "", "", false, "self", "<strong>$strcreatenewquestion</strong>");
$questionurl = new moodle_url("$CFG->wwwroot/question/question.php",
array('returnurl' => $returnurl,
'category' => $category->id));
if ($cm!==null){
$questionurl->param('cmid', $cm->id);
}
popup_form ($questionurl->out().'&amp;qtype=', $QTYPE_MENU, "addquestion", "", "choose", "", "", false, "self", "<strong>$strcreatenewquestion</strong>");
echo '</td><td valign="top" align="right">'; echo '</td><td valign="top" align="right">';
helpbutton("questiontypes", $strcreatenewquestion, "quiz"); helpbutton("questiontypes", $strcreatenewquestion, "quiz");
echo '</td>'; echo '</td>';
} }
else { else {
echo '<td>'; echo '<td>';
print_string("publishedit","quiz"); print_string('nopermissionadd', 'question');
echo '</td>'; echo '</td>';
} }
@ -265,6 +297,9 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage'); print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage');
echo question_sort_options($pageurl, $sortorder);
echo '<form method="post" action="edit.php">'; echo '<form method="post" action="edit.php">';
echo '<fieldset class="invisiblefieldset" style="display: block;">'; echo '<fieldset class="invisiblefieldset" style="display: block;">';
echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />'; echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
@ -272,12 +307,7 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
echo '<table id="categoryquestions" style="width: 100%"><tr>'; echo '<table id="categoryquestions" style="width: 100%"><tr>';
echo "<th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$straction</th>"; echo "<th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$straction</th>";
$sortoptions = array('alpha' => get_string("sortalpha", "quiz"), echo "<th style=\"white-space:nowrap; text-align: left;\" class=\"header\" scope=\"col\">$strquestionname</th>
'typealpha' => get_string("sorttypealpha", "quiz"),
'age' => get_string("sortage", "quiz"));
$orderselect = choose_from_menu ($sortoptions, 'qsortorder', $sortorder, false, 'this.form.submit();', '0', true);
$orderselect .= '<noscript><div><input type="submit" value="'.get_string("sortsubmit", "quiz").'" /></div></noscript>';
echo "<th style=\"white-space:nowrap; text-align: left;\" class=\"header\" scope=\"col\">$strquestionname $orderselect</th>
<th style=\"white-space:nowrap; text-align: right;\" class=\"header\" scope=\"col\">$strtype</th>"; <th style=\"white-space:nowrap; text-align: right;\" class=\"header\" scope=\"col\">$strtype</th>";
echo "</tr>\n"; echo "</tr>\n";
foreach ($questions as $question) { foreach ($questions as $question) {
@ -299,22 +329,32 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
echo "<tr>\n<td style=\"white-space:nowrap;\" $nameclass>\n"; echo "<tr>\n<td style=\"white-space:nowrap;\" $nameclass>\n";
$canuseq = question_has_capability_on($question, 'use', $question->category);
if (function_exists('module_specific_actions')) { if (function_exists('module_specific_actions')) {
echo module_specific_actions($pageurl, $question->id, $cm->id); echo module_specific_actions($pageurl, $question->id, $cm->id, $canuseq);
} }
// preview // preview
if ($canuseq) {
link_to_popup_window('/question/preview.php?id=' . $question->id . '&amp;quizid=' . $quizid, 'questionpreview', link_to_popup_window('/question/preview.php?id=' . $question->id . '&amp;quizid=' . $quizid, 'questionpreview',
"<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />", "<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />",
0, 0, $strpreview, QUESTION_PREVIEW_POPUP_OPTIONS); 0, 0, $strpreview, QUESTION_PREVIEW_POPUP_OPTIONS);
}
// edit, hide, delete question, using question capabilities, not quiz capabilieies // edit, hide, delete question, using question capabilities, not quiz capabilieies
if ($canedit) { if (question_has_capability_on($question, 'edit', $question->category) || question_has_capability_on($question, 'move', $question->category)) {
$questionparams = (($cm !== null)? array('cmid' => $cm->id) : array()) + echo "<a title=\"$stredit\" href=\"".$questionurl->out(false, array('id'=>$question->id))."\"><img
(array('returnurl' => $pageurl->out(), 'id'=>$question->id));
$questionurl = new moodle_url("$CFG->wwwroot/question/question.php", $questionparams);
echo "<a title=\"$stredit\" href=\"".$questionurl->out()."\"><img
src=\"$CFG->pixpath/t/edit.gif\" alt=\"$stredit\" /></a>&nbsp;"; src=\"$CFG->pixpath/t/edit.gif\" alt=\"$stredit\" /></a>&nbsp;";
} elseif (question_has_capability_on($question, 'view', $question->category)){
echo "<a title=\"$strview\" href=\"".$questionurl->out(false, array('id'=>$question->id))."\"><img
src=\"$CFG->pixpath/i/info.gif\" alt=\"$strview\" /></a>&nbsp;";
}
if (question_has_capability_on($question, 'move', $question->category) && question_has_capability_on($question, 'view', $question->category)) {
echo "<a title=\"$strmove\" href=\"".$questionurl->out(false, array('id'=>$question->id, 'movecontext'=>1))."\"><img
src=\"$CFG->pixpath/t/move.gif\" alt=\"$strmove\" /></a>&nbsp;";
}
if (question_has_capability_on($question, 'edit', $question->category)) {
// hide-feature // hide-feature
if($question->hidden) { if($question->hidden) {
echo "<a title=\"$strrestore\" href=\"edit.php?".$pageurl->get_query_string()."&amp;unhide=$question->id&amp;sesskey=$USER->sesskey\"><img echo "<a title=\"$strrestore\" href=\"edit.php?".$pageurl->get_query_string()."&amp;unhide=$question->id&amp;sesskey=$USER->sesskey\"><img
@ -324,7 +364,9 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
src=\"$CFG->pixpath/t/delete.gif\" alt=\"$strdelete\" /></a>"; src=\"$CFG->pixpath/t/delete.gif\" alt=\"$strdelete\" /></a>";
} }
} }
if ($caneditall || $canmoveall || $canuseall){
echo "&nbsp;<input title=\"$strselect\" type=\"checkbox\" name=\"q$question->id\" value=\"1\" />"; echo "&nbsp;<input title=\"$strselect\" type=\"checkbox\" name=\"q$question->id\" value=\"1\" />";
}
echo "</td>\n"; echo "</td>\n";
echo "<td $nameclass>" . format_string($question->name) . "</td>\n"; echo "<td $nameclass>" . format_string($question->name) . "</td>\n";
@ -338,14 +380,13 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
$formatoptions->noclean = true; $formatoptions->noclean = true;
$formatoptions->para = false; $formatoptions->para = false;
echo format_text($question->questiontext, $question->questiontextformat, echo format_text($question->questiontext, $question->questiontextformat,
$formatoptions, $course->id); $formatoptions, $COURSE->id);
echo "</td></tr>\n"; echo "</td></tr>\n";
} }
} }
echo "</table>\n"; echo "</table>\n";
$paging = print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage', $paging = print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage', false, true);
false, true);
if ($totalnumber > DEFAULT_QUESTIONS_PER_PAGE) { if ($totalnumber > DEFAULT_QUESTIONS_PER_PAGE) {
if ($perpage == DEFAULT_QUESTIONS_PER_PAGE) { if ($perpage == DEFAULT_QUESTIONS_PER_PAGE) {
$showall = '<a href="edit.php?'.$pageurl->get_query_string(array('qperpage'=>1000)).'">'.get_string('showall', 'moodle', $totalnumber).'</a>'; $showall = '<a href="edit.php?'.$pageurl->get_query_string(array('qperpage'=>1000)).'">'.get_string('showall', 'moodle', $totalnumber).'</a>';
@ -361,28 +402,146 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
} }
echo $paging; echo $paging;
echo '<table class="quiz-edit-selected"><tr><td colspan="2">'; if ($caneditall || $canmoveall || $canuseall){
echo '<a href="javascript:select_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectall.'</a> /'. echo '<a href="javascript:select_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectall.'</a> /'.
' <a href="javascript:deselect_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectnone.'</a>'. ' <a href="javascript:deselect_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectnone.'</a>';
'</td><td align="right"><b>&nbsp;'.get_string('withselected', 'quiz').':</b></td></tr><tr><td>'; echo '<br />';
echo '<strong>&nbsp;'.get_string('withselected', 'quiz').':</strong><br />';
if (function_exists('module_specific_buttons')) { if (function_exists('module_specific_buttons')) {
echo module_specific_buttons($cm->id); echo module_specific_buttons($cm->id);
} }
// print delete and move selected question // print delete and move selected question
if ($canedit) { if ($caneditall) {
echo '<input type="submit" name="deleteselected" value="'.$strdelete."\" /></td><td>\n"; echo '<input type="submit" name="deleteselected" value="'.$strdelete."\" />\n";
echo '<input type="submit" name="move" value="'.get_string('moveto', 'quiz')."\" />\n"; }
question_category_select_menu($course->id, false, true, $category->id); if ($canmoveall && count($addcontexts)) {
echo '<input type="submit" name="move" value="'.get_string('moveto', 'quiz')."\" />\n";
question_category_select_menu($addcontexts, false, 0, "$category->id,$category->contextid");
} }
echo "</td></tr></table>";
if (function_exists('module_specific_controls')) { if (function_exists('module_specific_controls') && $canuseall) {
echo module_specific_controls($totalnumber, $recurse, $category->id, $cm->id); echo module_specific_controls($totalnumber, $recurse, $category, $cm->id);
}
} }
echo '</fieldset>'; echo '</fieldset>';
echo "</form>\n"; echo "</form>\n";
} }
function question_sort_options($pageurl, $sortorder){
global $USER;
//sort options
$html = "<div class=\"mdl-align\">";
$html .= '<form method="post" action="edit.php">';
$html .= '<fieldset class="invisiblefieldset" style="display: block;">';
$html .= '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
$html .= $pageurl->hidden_params_out(array('qsortorder'));
$sortoptions = array('alpha' => get_string("sortalpha", "quiz"),
'typealpha' => get_string("sorttypealpha", "quiz"),
'age' => get_string("sortage", "quiz"));
$html .= choose_from_menu ($sortoptions, 'qsortorder', $sortorder, false, 'this.form.submit();', '0', true);
$html .= '<noscript><div><input type="submit" value="'.get_string("sortsubmit", "quiz").'" /></div></noscript>';
$html .= '</fieldset>';
$html .= "</form>\n";
$html .= "</div>\n";
return $html;
}
function question_showbank_actions($pageurl, $cm){
global $CFG;
/// Now, check for commands on this page and modify variables as necessary
if (isset($_REQUEST['move']) and confirm_sesskey()) { /// Move selected questions to new category
$category = required_param('category', PARAM_SEQUENCE);
list($tocategoryid, $contextid) = explode(',', $category);
if (! $tocategory = get_record('question_categories', 'id', $tocategoryid, 'contextid', $contextid)) {
error('Could not find category record');
}
$tocontext = get_context_instance_by_id($contextid);
require_capability('moodle/question:add', $tocontext);
$questionids = array();
foreach ($_POST as $key => $value) { // Parse input for question ids
if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
$key = $matches[1];
$questionids[] = $key;
}
}
if ($questionids){
$questionidlist = join($questionids, ',');
$sql = "SELECT q.*, c.contextid FROM {$CFG->prefix}question q, {$CFG->prefix}question_categories c WHERE q.id IN ($questionidlist) AND c.id = q.category";
if (!$questions = get_records_sql($sql)){
print_error('questiondoesnotexist', 'question', $pageurl->out());
}
$checkforfiles = false;
foreach ($questions as $question){
//check capabilities
question_require_capability_on($question, 'move');
$fromcontext = get_context_instance_by_id($question->contextid);
if (get_filesdir_from_context($fromcontext) != get_filesdir_from_context($tocontext)){
$checkforfiles = true;
}
}
$returnurl = $pageurl->out(false, array('category'=>"$tocategoryid,$contextid"));
if (!$checkforfiles){
foreach ($questionids as $questionid){
//move question
if (!set_field('question', 'category', $tocategory->id, 'id', $questionid)) {
error('Could not update category field');
}
}
redirect($returnurl);
} else {
$movecontexturl = new moodle_url($CFG->wwwroot.'/question/contextmoveq.php',
array('returnurl' => $returnurl,
'ids'=>$questionidlist,
'tocatid'=> $tocategoryid));
if ($cm){
$movecontexturl->param('cmid', $cm->id);
} else {
$movecontexturl->param('courseid', $COURSE->id);
}
redirect($movecontexturl->out());
}
}
}
if (isset($_REQUEST['deleteselected'])) { // delete selected questions from the category
if (isset($_REQUEST['confirm']) and confirm_sesskey()) { // teacher has already confirmed the action
$deleteselected = required_param('deleteselected');
if ($_REQUEST['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) {
question_require_capability_on($questionid, 'edit');
if (record_exists('quiz_question_instances', 'question', $questionid) or
record_exists('question_states', 'originalquestion', $questionid)) {
if (!set_field('question', 'hidden', 1, 'id', $questionid)) {
question_require_capability_on($questionid, 'edit');
error('Was not able to hide question');
}
} else {
delete_question($questionid);
}
}
}
redirect($pageurl->out());
} else {
error("Confirmation string was incorrect");
}
}
}
// Unhide a question
if(isset($_REQUEST['unhide']) && confirm_sesskey()) {
$unhide = required_param('unhide', PARAM_INT);
question_require_capability_on($unhide, 'edit');
if(!set_field('question', 'hidden', 0, 'id', $unhide)) {
error("Failed to unhide the question.");
}
redirect($pageurl->out());
}
}
/** /**
* Shows the question bank editing interface. * Shows the question bank editing interface.
* *
@ -400,55 +559,10 @@ function question_list($course, $pageurl, $categoryid, $cm = null,
* {@link http://maths.york.ac.uk/serving_maths} * {@link http://maths.york.ac.uk/serving_maths}
* @param moodle_url $pageurl object representing this pages url. * @param moodle_url $pageurl object representing this pages url.
*/ */
function question_showbank($pageurl, $cm, $page, $perpage, $sortorder, $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext){ function question_showbank($tabname, $contexts, $pageurl, $cm, $page, $perpage, $sortorder, $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext){
global $COURSE, $USER; global $COURSE;
/// Now, check for commands on this page and modify variables as necessary if (isset($_REQUEST['deleteselected'])){ // teacher still has to confirm
if (isset($_REQUEST['move']) and confirm_sesskey()) { /// Move selected questions to new category
$tocategoryid = required_param('category', PARAM_INT);
if (!$tocategory = get_record('question_categories', 'id', $tocategoryid)) {
error('Invalid category');
}
if (!has_capability('moodle/question:managecategory', get_context_instance(CONTEXT_COURSE, $tocategory->course))){
error(get_string('categorynoedit', 'quiz', $tocategory->name), $pageurl->out());
}
foreach ($_POST as $key => $value) { // Parse input for question ids
if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
$key = $matches[1];
if (!set_field('question', 'category', $tocategory->id, 'id', $key)) {
error('Could not update category field');
}
}
}
}
if (isset($_REQUEST['deleteselected'])) { // delete selected questions from the category
if (isset($_REQUEST['confirm']) and confirm_sesskey()) { // teacher has already confirmed the action
$deleteselected = required_param('deleteselected');
if ($_REQUEST['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('question_states', 'originalquestion', $questionid)) {
if (!set_field('question', 'hidden', 1, 'id', $questionid)) {
error('Was not able to hide question');
}
} else {
delete_question($questionid);
}
}
}
echo '</td></tr>';
echo '</table>';
echo '</div>';
redirect($pageurl->out());
} else {
error("Confirmation string was incorrect");
}
} else { // teacher still has to confirm
// make a list of all the questions that are selected // make a list of all the questions that are selected
$rawquestions = $_REQUEST; $rawquestions = $_REQUEST;
$questionlist = ''; // comma separated list of ids of questions to be deleted $questionlist = ''; // comma separated list of ids of questions to be deleted
@ -458,6 +572,7 @@ function question_showbank($pageurl, $cm, $page, $perpage, $sortorder, $sortorde
foreach ($rawquestions as $key => $value) { // Parse input for question ids foreach ($rawquestions as $key => $value) { // Parse input for question ids
if (preg_match('!^q([0-9]+)$!', $key, $matches)) { if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
$key = $matches[1]; $questionlist .= $key.','; $key = $matches[1]; $questionlist .= $key.',';
question_require_capability_on($key, 'edit');
if (record_exists('quiz_question_instances', 'question', $key) or if (record_exists('quiz_question_instances', 'question', $key) or
record_exists('question_states', 'originalquestion', $key)) { record_exists('question_states', 'originalquestion', $key)) {
$questionnames .= '* '; $questionnames .= '* ';
@ -476,47 +591,38 @@ function question_showbank($pageurl, $cm, $page, $perpage, $sortorder, $sortorde
$questionnames .= '<br />'.get_string('questionsinuse', 'quiz'); $questionnames .= '<br />'.get_string('questionsinuse', 'quiz');
} }
notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames), notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames),
$pageurl->out(), $pageurl->out(), $pageurl->out_action(array('deleteselected'=>$questionlist, 'confirm'=>md5($questionlist))),
array('sesskey' => $USER->sesskey, 'deleteselected' => $questionlist, 'confirm' => md5($questionlist)), $pageurl->out_action());
NULL, 'post', 'get');
echo '</td></tr>'; echo '</td></tr>';
echo '</table>'; echo '</table>';
print_footer($COURSE); print_footer($COURSE);
exit; exit;
} }
}
// Unhide a question
if(isset($_REQUEST['unhide']) && confirm_sesskey()) {
$unhide = required_param('unhide', PARAM_INT);
if(!set_field('question', 'hidden', 0, 'id', $unhide)) {
error("Failed to unhide the question.");
}
redirect($pageurl->out());
}
// starts with category selection form // starts with category selection form
print_box_start('generalbox questionbank'); print_box_start('generalbox questionbank');
print_heading(get_string('questionbank', 'question'), '', 2); print_heading(get_string('questionbank', 'question'), '', 2);
question_category_form($COURSE, $pageurl, $cat, $recurse, question_category_form($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, $recurse, $showhidden, $showquestiontext);
$showhidden, $showquestiontext);
// continues with list of questions // continues with list of questions
question_list($COURSE, $pageurl, $cat, isset($cm) ? $cm : null, question_list($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, isset($cm) ? $cm : null,
$recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded, $recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded, $showquestiontext,
$showquestiontext); $contexts->having_cap('moodle/question:add'));
print_box_end(); print_box_end();
} }
/** /**
* Common setup for all pages for editing questions. * Common setup for all pages for editing questions.
* @param string $edittab code for this edit tab
* @param boolean $requirecmid require cmid? default false * @param boolean $requirecmid require cmid? default false
* @param boolean $requirecourseid require courseid, if cmid is not given? default true * @param boolean $requirecourseid require courseid, if cmid is not given? default true
* @return array $thispageurl, $courseid, $cmid, $cm, $module, $pagevars * @return array $thispageurl, $contexts, $cmid, $cm, $module, $pagevars
*/ */
function question_edit_setup($requirecmid = false, $requirecourseid = true){ function question_edit_setup($edittab, $requirecmid = false, $requirecourseid = true){
global $COURSE; global $COURSE, $QUESTION_EDITTABCAPS;
//$thispageurl is used to construct urls for all question edit pages we link to from this page. It contains an array //$thispageurl is used to construct urls for all question edit pages we link to from this page. It contains an array
//of parameters that are passed from page to page. //of parameters that are passed from page to page.
$thispageurl = new moodle_url(); $thispageurl = new moodle_url();
@ -529,6 +635,8 @@ function question_edit_setup($requirecmid = false, $requirecourseid = true){
list($module, $cm) = get_module_from_cmid($cmid); list($module, $cm) = get_module_from_cmid($cmid);
$courseid = $cm->course; $courseid = $cm->course;
$thispageurl->params(compact('cmid')); $thispageurl->params(compact('cmid'));
require_login($courseid, false, $cm);
$thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
} else { } else {
$module = null; $module = null;
$cm = null; $cm = null;
@ -539,9 +647,21 @@ function question_edit_setup($requirecmid = false, $requirecourseid = true){
} }
if ($courseid){ if ($courseid){
$thispageurl->params(compact('courseid')); $thispageurl->params(compact('courseid'));
}
}
require_login($courseid, false); require_login($courseid, false);
$thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
} else {
$thiscontext = null;
}
}
if ($thiscontext){
$contexts = new question_edit_contexts($thiscontext);
$contexts->require_one_edit_tab_cap($edittab);
} else {
$contexts = null;
}
$pagevars['qpage'] = optional_param('qpage', -1, PARAM_INT); $pagevars['qpage'] = optional_param('qpage', -1, PARAM_INT);
@ -549,8 +669,8 @@ function question_edit_setup($requirecmid = false, $requirecourseid = true){
//pass 'cat' from page to page and when 'category' comes from a drop down menu //pass 'cat' from page to page and when 'category' comes from a drop down menu
//then we also reset the qpage so we go to page 1 of //then we also reset the qpage so we go to page 1 of
//a new cat. //a new cat.
$pagevars['cat'] = optional_param('cat', 0, PARAM_INT); $pagevars['cat'] = optional_param('cat', 0, PARAM_SEQUENCE);// if empty will be set up later
if ($category = optional_param('category', 0, PARAM_INT)){ if ($category = optional_param('category', 0, PARAM_SEQUENCE)){
$pagevars['cat'] = $category; $pagevars['cat'] = $category;
$pagevars['qpage'] = 0; $pagevars['qpage'] = 0;
} }
@ -583,11 +703,21 @@ function question_edit_setup($requirecmid = false, $requirecourseid = true){
$pagevars['qsortorder'] = 'typealpha'; $pagevars['qsortorder'] = 'typealpha';
} }
$defaultcategory = question_make_default_categories($contexts->all());
if (empty($pagevars['cat']) or !count_records_select("question_categories", "id = '".$pagevars['cat']."' AND (course = '{$COURSE->id}' OR publish = '1')")) { $contextlistarr = array();
$category = get_default_question_category($COURSE->id); foreach ($contexts->having_one_edit_tab_cap($edittab) as $context){
$pagevars['cat'] = $category->id; $contextlistarr[] = "'$context->id'";
$thispageurl->param('cat', $category->id); }
$contextlist = join($contextlistarr, ' ,');
if (!empty($pagevars['cat'])){
$catparts = explode(',', $pagevars['cat']);
if (!$catparts[0] || (FALSE !== array_search($catparts[1], $contextlistarr)) || !count_records_select("question_categories", "id = '".$catparts[0]."' AND contextid = $catparts[1]")) {
error(get_string('invalidcategory', 'quiz'));
}
} else {
$category = $defaultcategory;
$pagevars['cat'] = "$category->id,$category->contextid";
} }
if(($recurse = optional_param('recurse', -1, PARAM_BOOL)) != -1) { if(($recurse = optional_param('recurse', -1, PARAM_BOOL)) != -1) {
@ -618,6 +748,191 @@ function question_edit_setup($requirecmid = false, $requirecourseid = true){
} }
return array($thispageurl, $courseid, $cmid, $cm, $module, $pagevars); return array($thispageurl, $contexts, $cmid, $cm, $module, $pagevars);
}
class question_edit_contexts{
var $allcontexts;
/**
* @param current context
*/
function question_edit_contexts($thiscontext){
$pcontextids = get_parent_contexts($thiscontext);
$contexts = array($thiscontext);
foreach ($pcontextids as $pcontextid){
$contexts[] = get_context_instance_by_id($pcontextid);
}
$this->allcontexts = $contexts;
}
/**
* @return array all parent contexts
*/
function all(){
return $this->allcontexts;
}
/**
* @return object lowest context which must be either the module or course context
*/
function lowest(){
return $this->allcontexts[0];
}
/**
* @param string $cap capability
* @return array parent contexts having capability, zero based index
*/
function having_cap($cap){
$contextswithcap = array();
foreach ($this->allcontexts as $context){
if (has_capability($cap, $context)){
$contextswithcap[] = $context;
}
}
return $contextswithcap;
}
/**
* @param array $caps capabilities
* @return array parent contexts having at least one of $caps, zero based index
*/
function having_one_cap($caps){
$contextswithacap = array();
foreach ($this->allcontexts as $context){
foreach ($caps as $cap){
if (has_capability($cap, $context)){
$contextswithacap[] = $context;
break; //done with caps loop
}
}
}
return $contextswithacap;
}
/**
* @param string $tabname edit tab name
* @return array parent contexts having at least one of $caps, zero based index
*/
function having_one_edit_tab_cap($tabname){
global $QUESTION_EDITTABCAPS;
return $this->having_one_cap($QUESTION_EDITTABCAPS[$tabname]);
}
/**
* Has at least one parent context got the cap $cap?
*
* @param string $cap capability
* @return boolean
*/
function have_cap($cap){
return (count($this->having_cap($cap)));
}
/**
* Has at least one parent context got one of the caps $caps?
*
* @param string $cap capability
* @return boolean
*/
function have_one_cap($caps){
foreach ($caps as $cap){
if ($this->have_cap($cap)){
return true;
}
}
return false;
}
/**
* Has at least one parent context got one of the caps for actions on $tabname
*
* @param string $tabname edit tab name
* @return boolean
*/
function have_one_edit_tab_cap($tabname){
global $QUESTION_EDITTABCAPS;
return $this->have_one_cap($QUESTION_EDITTABCAPS[$tabname]);
}
/**
* Throw error if at least one parent context hasn't got the cap $cap
*
* @param string $cap capability
*/
function require_cap($cap){
if (!$this->have_cap($cap)){
print_error('nopermissions', '', '', $cap);
}
}
/**
* Throw error if at least one parent context hasn't got one of the caps $caps
*
* @param array $cap capabilities
*/
function require_one_cap($caps){
if (!$this->have_one_cap($caps)){
$capsstring = join($caps, ', ');
print_error('nopermissions', '', '', $capsstring);
}
}
/**
* Throw error if at least one parent context hasn't got one of the caps $caps
*
* @param string $tabname edit tab name
*/
function require_one_edit_tab_cap($tabname){
if (!$this->have_one_edit_tab_cap($tabname)){
print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
}
}
}
//capabilities for each page of edit tab.
//this determines which contexts' categories are available. At least one
//page is displayed if user has one of the capability on at least one context
$QUESTION_EDITTABCAPS = array(
'editq' => array('moodle/question:add',
'moodle/question:editmine',
'moodle/question:editall',
'moodle/question:viewmine',
'moodle/question:viewall',
'moodle/question:usemine',
'moodle/question:useall',
'moodle/question:movemine',
'moodle/question:moveall'),
'questions'=>array('moodle/question:add',
'moodle/question:editmine',
'moodle/question:editall',
'moodle/question:viewmine',
'moodle/question:viewall',
'moodle/question:movemine',
'moodle/question:moveall'),
'categories'=>array('moodle/question:managecategory'),
'import'=>array('moodle/question:add'),
'export'=>array('moodle/question:viewall', 'moodle/question:viewmine'));
/**
* Make sure user is logged in as required in this context.
*/
function require_login_in_context($contextorid = null){
if (!is_object($contextorid)){
$context = get_context_instance_by_id($contextorid);
} else {
$context = $contextorid;
}
if ($context && ($context->contextlevel == CONTEXT_COURSE)) {
require_login($context->instanceid);
} else if ($context && ($context->contextlevel == CONTEXT_MODULE)) {
if ($cm = get_record('course_modules','id',$context->instanceid)) {
if (!$course = get_record('course', 'id', $cm->course)) {
error('Incorrect course.');
}
require_course_login($course, true, $cm);
} else {
error('Incorrect course module id.');
}
} else if ($context && ($context->contextlevel == CONTEXT_SYSTEM)) {
if (!empty($CFG->forcelogin)) {
require_login();
}
} else {
require_login();
}
} }
?> ?>

View file

@ -10,66 +10,44 @@
*/ */
require_once("../config.php"); require_once("../config.php");
require_once( "editlib.php" ); require_once("editlib.php");
require_once("export_form.php");
list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup(); list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('export');
$cattofile = optional_param('cattofile',0, PARAM_BOOL);
$exportfilename = optional_param('exportfilename','',PARAM_FILE );
$format = optional_param('format','', PARAM_FILE );
$categoryid = optional_param('category',0,PARAM_INT);
// get display strings // get display strings
$txt = new object; $txt = new object;
$txt->category = get_string('category','quiz'); $txt->category = get_string('category', 'quiz');
$txt->download = get_string('download','quiz'); $txt->download = get_string('download', 'quiz');
$txt->downloadextra = get_string('downloadextra','quiz'); $txt->downloadextra = get_string('downloadextra', 'quiz');
$txt->exporterror = get_string('exporterror','quiz'); $txt->exporterror = get_string('exporterror', 'quiz');
$txt->exportname = get_string('exportname','quiz'); $txt->exportname = get_string('exportname', 'quiz');
$txt->exportquestions = get_string('exportquestions', 'quiz'); $txt->exportquestions = get_string('exportquestions', 'quiz');
$txt->fileformat = get_string('fileformat','quiz'); $txt->fileformat = get_string('fileformat', 'quiz');
$txt->exportcategory = get_string('exportcategory','quiz'); $txt->exportcategory = get_string('exportcategory', 'quiz');
$txt->modulename = get_string('modulename','quiz'); $txt->modulename = get_string('modulename', 'quiz');
$txt->modulenameplural = get_string('modulenameplural','quiz'); $txt->modulenameplural = get_string('modulenameplural', 'quiz');
$txt->tofile = get_string('tofile','quiz'); $txt->tofile = get_string('tofile', 'quiz');
if (!$course = get_record("course", "id", $courseid)) {
error("Course does not exist!");
}
// make sure we are using the user's most recent category choice // make sure we are using the user's most recent category choice
if (empty($categoryid)) { if (empty($categoryid)) {
$categoryid = $pagevars['cat']; $categoryid = $pagevars['cat'];
} }
if (!$category = get_record("question_categories", "id", $categoryid)) {
$category = get_default_question_category($courseid);
}
if (!$categorycourse = get_record("course", "id", $category->course)) {
print_error('nocategory','quiz');
}
// check role capability
$context = get_context_instance(CONTEXT_COURSE, $course->id);
require_capability('moodle/question:export', $context);
// ensure the files area exists for this course // ensure the files area exists for this course
make_upload_directory( "$course->id" ); make_upload_directory("$COURSE->id");
list($catid, $catcontext) = explode(',', $pagevars['cat']);
// check category is valid if (!$category = get_record("question_categories", "id", $catid, 'contextid', $catcontext)) {
$validcats = question_category_options( $course->id, true, false ); print_error('nocategory','quiz');
if (!array_key_exists( $categoryid, $validcats)) {
print_error( 'invalidcategory','quiz' );
} }
/// Header /// Header
if ($cm!==null) { if ($cm!==null) {
$strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id)) $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', $cm->modname)) ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: ""; : "";
$navlinks = array(); $navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity'); $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity');
@ -94,112 +72,73 @@
include('tabs.php'); include('tabs.php');
} }
if (!empty($format)) { /// Filename $exportfilename = default_export_filename($COURSE, $category);
$export_form = new question_export_form($thispageurl, array('contexts'=>$contexts->having_one_edit_tab_cap('export'), 'defaultcategory'=>$pagevars['cat'],
'defaultfilename'=>$exportfilename));
if (!confirm_sesskey()) {
print_error( 'sesskey' ); if ($from_form = $export_form->get_data()) { /// Filename
if (! is_readable("format/$from_form->format/format.php")) {
error("Format not known ($from_form->format)");
} }
if (! is_readable("format/$format/format.php")) {
error( "Format not known ($format)" ); }
// load parent class for import/export // load parent class for import/export
require("format.php"); require_once("format.php");
// and then the class for the selected format // and then the class for the selected format
require("format/$format/format.php"); require_once("format/$from_form->format/format.php");
$classname = "qformat_$format"; $classname = "qformat_$from_form->format";
$qformat = new $classname(); $qformat = new $classname();
$qformat->setContexts($contexts->having_one_edit_tab_cap('export'));
$qformat->setCategory($category);
$qformat->setCourse($COURSE);
$qformat->setCategory( $category ); if (empty($from_form->exportfilename)) {
$qformat->setCourse( $course ); $from_form->exportfilename = default_export_filename($COURSE, $category);
$qformat->setFilename( $exportfilename ); }
$qformat->setCattofile( $cattofile ); $qformat->setFilename($from_form->exportfilename);
$qformat->setCattofile(!empty($from_form->cattofile));
$qformat->setContexttofile(!empty($from_form->contexttofile));
if (! $qformat->exportpreprocess()) { // Do anything before that we need to if (! $qformat->exportpreprocess()) { // Do anything before that we need to
error( $txt->exporterror, $thispageurl->out()); error($txt->exporterror, $thispageurl->out());
} }
if (! $qformat->exportprocess()) { // Process the export data if (! $qformat->exportprocess()) { // Process the export data
error( $txt->exporterror, $thispageurl->out()); error($txt->exporterror, $thispageurl->out());
} }
if (! $qformat->exportpostprocess()) { // In case anything needs to be done after if (! $qformat->exportpostprocess()) { // In case anything needs to be done after
error( $txt->exporterror, $thispageurl->out()); error($txt->exporterror, $thispageurl->out());
} }
echo "<hr />"; echo "<hr />";
// link to download the finished file // link to download the finished file
$file_ext = $qformat->export_file_extension(); $file_ext = $qformat->export_file_extension();
if ($CFG->slasharguments) { if ($CFG->slasharguments) {
$efile = "{$CFG->wwwroot}/file.php/".$qformat->question_get_export_dir()."/$exportfilename".$file_ext."?forcedownload=1"; $efile = "{$CFG->wwwroot}/file.php/".$qformat->question_get_export_dir()."/$from_form->exportfilename".$file_ext."?forcedownload=1";
} }
else { else {
$efile = "{$CFG->wwwroot}/file.php?file=/".$qformat->question_get_export_dir()."/$exportfilename".$file_ext."&forcedownload=1"; $efile = "{$CFG->wwwroot}/file.php?file=/".$qformat->question_get_export_dir()."/$from_form->exportfilename".$file_ext."&forcedownload=1";
} }
echo "<p><div class=\"boxaligncenter\"><a href=\"$efile\">$txt->download</a></div></p>"; echo "<p><div class=\"boxaligncenter\"><a href=\"$efile\">$txt->download</a></div></p>";
echo "<p><div class=\"boxaligncenter\"><font size=\"-1\">$txt->downloadextra</font></div></p>"; echo "<p><div class=\"boxaligncenter\"><font size=\"-1\">$txt->downloadextra</font></div></p>";
print_continue("edit.php?".$thispageurl->get_query_string()); print_continue("edit.php?".$thispageurl->get_query_string());
print_footer($course); print_footer($COURSE);
exit; exit;
} }
/// Display upload form /// Display export form
// get valid formats to generate dropdown list
$fileformatnames = get_import_export_formats( 'export' );
// get filename
if (empty($exportfilename)) {
$exportfilename = default_export_filename($course, $category);
}
print_heading_with_help($txt->exportquestions, 'export', 'quiz'); print_heading_with_help($txt->exportquestions, 'export', 'quiz');
print_simple_box_start('center');
?> $export_form->display();
<form enctype="multipart/form-data" method="post" action="export.php"> print_footer($COURSE);
<fieldset class="invisiblefieldset" style="display: block;">
<input type="hidden" name="sesskey" value="<?php echo sesskey(); ?>" />
<?php echo $thispageurl->hidden_params_out(array(), 3); ?>
<table cellpadding="5">
<tr>
<td align="right"><?php echo $txt->category; ?>:</td>
<td>
<?php
question_category_select_menu($course->id, true, false, $category->id);
echo $txt->tofile; ?>
<input name="cattofile" type="checkbox" />
<?php helpbutton('exportcategory', $txt->exportcategory, 'quiz'); ?>
</td>
</tr>
<tr>
<td align="right"><?php echo $txt->fileformat; ?>:</td>
<td>
<?php choose_from_menu($fileformatnames, 'format', 'gift', '');
helpbutton('export', $txt->exportquestions, 'quiz'); ?>
</td>
</tr>
<tr>
<td align="right"><?php echo $txt->exportname; ?>:</td>
<td>
<input type="text" size="40" name="exportfilename" value="<?php echo $exportfilename; ?>" />
</td>
</tr>
<tr>
<td align="center" >
<input type="submit" name="save" value="<?php echo $txt->exportquestions; ?>" />
</td>
<td>&nbsp;</td>
</tr>
</table>
</fieldset>
</form>
<?php
print_simple_box_end();
print_footer($course);
?> ?>

View file

@ -16,10 +16,14 @@ class qformat_default {
var $filename = ''; var $filename = '';
var $matchgrades = 'error'; var $matchgrades = 'error';
var $catfromfile = 0; var $catfromfile = 0;
var $contextfromfile = 0;
var $cattofile = 0; var $cattofile = 0;
var $contexttofile = 0;
var $questionids = array(); var $questionids = array();
var $importerrors = 0; var $importerrors = 0;
var $stoponerror = true; var $stoponerror = true;
var $translator = null;
// functions to indicate import/export functionality // functions to indicate import/export functionality
// override to return true if implemented // override to return true if implemented
@ -49,6 +53,14 @@ class qformat_default {
function setCourse( $course ) { function setCourse( $course ) {
$this->course = $course; $this->course = $course;
} }
/**
* set an array of contexts.
* @param array $contexts Moodle course variable
*/
function setContexts($contexts) {
$this->contexts = $contexts;
$this->translator = new context_to_string_translator($this->contexts);
}
/** /**
* set the filename * set the filename
@ -74,6 +86,14 @@ class qformat_default {
$this->catfromfile = $catfromfile; $this->catfromfile = $catfromfile;
} }
/**
* set contextfromfile
* @param bool $contextfromfile allow contexts embedded in import file
*/
function setContextfromfile($contextfromfile) {
$this->contextfromfile = $contextfromfile;
}
/** /**
* set cattofile * set cattofile
* @param bool cattofile exports categories within export file * @param bool cattofile exports categories within export file
@ -81,6 +101,13 @@ class qformat_default {
function setCattofile( $cattofile ) { function setCattofile( $cattofile ) {
$this->cattofile = $cattofile; $this->cattofile = $cattofile;
} }
/**
* set contexttofile
* @param bool cattofile exports categories within export file
*/
function setContexttofile($contexttofile) {
$this->contexttofile = $contexttofile;
}
/** /**
* set stoponerror * set stoponerror
@ -153,6 +180,7 @@ class qformat_default {
* @return boolean success * @return boolean success
*/ */
function importprocess() { function importprocess() {
global $USER;
// reset the timer in case file upload was slow // reset the timer in case file upload was slow
@set_time_limit(); @set_time_limit();
@ -194,7 +222,7 @@ class qformat_default {
if ($this->catfromfile) { if ($this->catfromfile) {
// find/create category object // find/create category object
$catpath = $question->category; $catpath = $question->category;
$newcategory = create_category_path( $catpath, '/', $this->course->id ); $newcategory = $this->create_category_path( $catpath, '/');
if (!empty($newcategory)) { if (!empty($newcategory)) {
$this->category = $newcategory; $this->category = $newcategory;
} }
@ -231,6 +259,9 @@ class qformat_default {
$question->category = $this->category->id; $question->category = $this->category->id;
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question->createdby = $USER->id;
$question->timecreated = time();
if (!$question->id = insert_record("question", $question)) { if (!$question->id = insert_record("question", $question)) {
error( get_string('cannotinsert','quiz') ); error( get_string('cannotinsert','quiz') );
} }
@ -258,7 +289,57 @@ class qformat_default {
} }
return true; return true;
} }
/**
* find and/or create the category described by a delimited list
* e.g. $course$/tom/dick/harry or tom/dick/harry
*
* removes any context string no matter whether $getcontext is set
* but if $getcontext is set then ignore the context and use selected category context.
*
* @param string catpath delimited category path
* @param string delimiter path delimiting character
* @param int courseid course to search for categories
* @return mixed category object or null if fails
*/
function create_category_path($catpath, $delimiter='/') {
$catpath = clean_param($catpath, PARAM_PATH);
$catnames = explode($delimiter, $catpath);
$parent = 0;
$category = null;
if (FALSE !== preg_match('/^\$([a-z]+)\$$/', $catnames[0], $matches)){
$contextid = $this->translator->string_to_context($matches[1]);
array_shift($catnames);
} else {
$contextid = FALSE;
}
if ($this->contextfromfile && ($contextid !== FALSE)){
$context = get_context_instance_by_id($contextid);
require_capability('moodle/question:add', $context);
} else {
$context = get_context_instance_by_id($this->category->contextid);
}
foreach ($catnames as $catname) {
if ($category = get_record( 'question_categories', 'name', $catname, 'contextid', $context->id, 'parent', $parent)) {
$parent = $category->id;
} else {
require_capability('moodle/question:managecategory', $context);
// create the new category
$category = new object;
$category->contextid = $context->id;
$category->name = $catname;
$category->info = '';
$category->parent = $parent;
$category->sortorder = 999;
$category->stamp = make_unique_id_code();
if (!($id = insert_record('question_categories', $category))) {
error( "cannot create new category - $catname" );
}
$category->id = $id;
$parent = $id;
}
}
return $category;
}
/** /**
* Return complete file within an array, one item per line * Return complete file within an array, one item per line
* @param string filename name of file * @param string filename name of file
@ -526,7 +607,7 @@ class qformat_default {
if ($this->cattofile) { if ($this->cattofile) {
if ($question->category != $trackcategory) { if ($question->category != $trackcategory) {
$trackcategory = $question->category; $trackcategory = $question->category;
$categoryname = get_category_path( $trackcategory ); $categoryname = $this->get_category_path($trackcategory, '/', $this->contexttofile);
// create 'dummy' question for category export // create 'dummy' question for category export
$dummyquestion = new object; $dummyquestion = new object;
@ -559,6 +640,35 @@ class qformat_default {
fclose($fh); fclose($fh);
return true; return true;
} }
/**
* get the category as a path (e.g., tom/dick/harry)
* @param int id the id of the most nested catgory
* @param string delimiter the delimiter you want
* @return string the path
*/
function get_category_path($id, $delimiter='/', $includecontext = true) {
$path = '';
if (!$firstcategory = get_record('question_categories','id',$id)) {
print_error( "Error getting category record from db - $id" );
}
$category = $firstcategory;
$contextstring = $this->translator->context_to_string($category->contextid);
do {
$name = $category->name;
$id = $category->parent;
if (!empty($path)) {
$path = "{$name}{$delimiter}{$path}";
}
else {
$path = $name;
}
} while ($category = get_record( 'question_categories','id',$id ));
if ($includecontext){
$path = '$'.$contextstring.'$'."{$delimiter}{$path}";
}
return $path;
}
/** /**
* Do an post-processing that may be required * Do an post-processing that may be required
@ -579,7 +689,6 @@ class qformat_default {
// if not overidden, then this is an error. // if not overidden, then this is an error.
$formatnotimplemented = get_string( 'formatnotimplemented','quiz' ); $formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
echo "<p>$formatnotimplemented</p>"; echo "<p>$formatnotimplemented</p>";
return NULL; return NULL;
} }
@ -608,6 +717,8 @@ class qformat_default {
} }
return format_text(stripslashes($question->questiontext), $format, $formatoptions); return format_text(stripslashes($question->questiontext), $format, $formatoptions);
} }
} }
?> ?>

View file

@ -25,7 +25,7 @@ class qformat_coursetestmanager extends qformat_default {
} }
function importprocess($filename) { function importprocess($filename) {
global $CFG,$strimportquestions,$form,$question_category,$category,$course, global $CFG, $USER, $strimportquestions,$form,$question_category,$category,$course,
$hostname, $mdapath, $mdbpath; $hostname, $mdapath, $mdbpath;
if ((PHP_OS == "Linux") and isset($hostname)) { if ((PHP_OS == "Linux") and isset($hostname)) {
$hostname = trim($hostname); $hostname = trim($hostname);
@ -261,6 +261,8 @@ class qformat_coursetestmanager extends qformat_default {
echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>"; echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$question->category = $this->category->id; $question->category = $this->category->id;
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question->createdby = $USER->id;
$question->timecreated = time();
if (!$question->id = insert_record("question", $question)) { if (!$question->id = insert_record("question", $question)) {
error("Could not insert new question!"); error("Could not insert new question!");
} }

View file

@ -10,92 +10,53 @@
*/ */
require_once("../config.php"); require_once("../config.php");
require_once("editlib.php" ); require_once("editlib.php");
require_once($CFG->libdir . '/uploadlib.php'); require_once($CFG->libdir . '/uploadlib.php');
require_once($CFG->libdir . '/questionlib.php'); require_once($CFG->libdir . '/questionlib.php');
require_once("import_form.php");
list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup(false, false); list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('import', false, false);
// get parameters
$params = new stdClass;
$params->choosefile = optional_param('choosefile','',PARAM_PATH);
$catfromfile = optional_param('catfromfile', 0, PARAM_BOOL );
$format = optional_param('format','',PARAM_FILE);
$params->matchgrades = optional_param('matchgrades','',PARAM_ALPHA);
$params->stoponerror = optional_param('stoponerror', 0, PARAM_BOOL);
$params->category = optional_param( 'category', 0, PARAM_INT );
// get display strings // get display strings
$txt = new stdClass(); $txt = new stdClass();
$txt->category = get_string('category','quiz');
$txt->choosefile = get_string('choosefile','quiz');
$txt->file = get_string('file');
$txt->fileformat = get_string('fileformat','quiz');
$txt->fromfile = get_string('fromfile','quiz');
$txt->importcategory = get_string('importcategory','quiz');
$txt->importerror = get_string('importerror','quiz'); $txt->importerror = get_string('importerror','quiz');
$txt->importfilearea = get_string('importfilearea','quiz');
$txt->importfileupload = get_string('importfileupload','quiz');
$txt->importfromthisfile = get_string('importfromthisfile','quiz');
$txt->importquestions = get_string("importquestions", "quiz"); $txt->importquestions = get_string("importquestions", "quiz");
$txt->matchgrades = get_string('matchgrades','quiz');
$txt->matchgradeserror = get_string('matchgradeserror','quiz');
$txt->matchgradesnearest = get_string('matchgradesnearest','quiz');
$txt->modulename = get_string('modulename','quiz');
$txt->modulenameplural = get_string('modulenameplural','quiz');
$txt->onlyteachersimport = get_string('onlyteachersimport','quiz');
$txt->questions = get_string("questions", "quiz");
$txt->quizzes = get_string('modulenameplural', 'quiz');
$txt->stoponerror = get_string('stoponerror', 'quiz');
$txt->upload = get_string('upload');
$txt->uploadproblem = get_string('uploadproblem');
$txt->uploadthisfile = get_string('uploadthisfile');
// matching options list($catid, $catcontext) = explode(',', $pagevars['cat']);
$matchgrades = array(); if (!$category = get_record("question_categories", "id", $catid)) {
$matchgrades['error'] = $txt->matchgradeserror;
$matchgrades['nearest'] = $txt->matchgradesnearest;
// not sure where $pagevars['cat'] comes from, but it doesn't respect
// the user's choice on the form - so this bodge
if (empty($params->category)) {
$params->category = $pagevars['cat'];
}
if (!$category = get_record("question_categories", "id", $params->category)) {
// if no valid category was given, use the default category
print_error('nocategory','quiz'); print_error('nocategory','quiz');
} }
// check category is valid (against THIS courseid, before we change it) //this page can be called without courseid or cmid in which case
$validcats = question_category_options( $cmid, false, true ); //we get the context from the category object.
if (!array_key_exists( $params->category, $validcats )) { if ($contexts === null) { // need to get the course from the chosen category
print_error( 'invalidcategory', 'quiz' ); $contexts = new question_edit_contexts(get_context_instance_by_id($category->contextid));
$thiscontext = $contexts->lowest();
if ($thiscontext->contextlevel == CONTEXT_COURSE){
require_login($thiscontext->instanceid, false);
} elseif ($thiscontext->contextlevel == CONTEXT_MODULE){
list($module, $cm) = get_module_from_cmid($thiscontext->instanceid);
require_login($cm->course, false, $cm);
} }
$contexts->require_one_edit_tab_cap($edittab);
$localcourseid = $cmid;
$courseid = $category->course;
if (!$course = get_record("course", "id", $courseid)) {
error("Invalid course!");
} }
require_login($course->id, false);
$context = get_context_instance(CONTEXT_COURSE, $course->id);
require_capability('moodle/question:import', $context);
// ensure the files area exists for this course // ensure the files area exists for this course
make_upload_directory( "$course->id" ); make_upload_directory("$COURSE->id");
$import_form = new question_import_form($thispageurl, array('contexts'=>$contexts->having_one_edit_tab_cap('import'),
'defaultcategory'=>$pagevars['cat']));
if ($import_form->is_cancelled()){
redirect($thispageurl);
}
//========== //==========
// PAGE HEADER // PAGE HEADER
//========== //==========
if ($cm!==null) { if ($cm!==null) {
$strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id)) $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $COURSE->id))
? update_module_button($cm->id, $course->id, get_string('modulename', $cm->modname)) ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: ""; : "";
$navlinks = array(); $navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity'); $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity');
@ -122,31 +83,25 @@
// file upload form sumitted // file upload form sumitted
if (!empty($format) and confirm_sesskey() ) { if ($form = $import_form->get_data()) {
// file checks out ok // file checks out ok
$fileisgood = false; $fileisgood = false;
// work out if this is an uploaded file // work out if this is an uploaded file
// or one from the filesarea. // or one from the filesarea.
if (!empty($params->choosefile)) { if (!empty($form->choosefile)) {
$importfile = "{$CFG->dataroot}/{$course->id}/{$params->choosefile}"; $importfile = "{$CFG->dataroot}/{$COURSE->id}/{$form->choosefile}";
if (file_exists($importfile)) { if (file_exists($importfile)) {
$fileisgood = true; $fileisgood = true;
} } else {
else { error(get_string('uploadproblem', 'moodle', $form->choosefile));
notify($txt->uploadproblem);
} }
} else { } else {
// must be upload file // must be upload file
if (empty($_FILES['newfile'])) { if (!$importfile = $import_form->get_importfile_name()) {
notify( $txt->uploadproblem ); error(get_string('uploadproblem', 'moodle'));
} }else {
else if ((!is_uploaded_file($_FILES['newfile']['tmp_name']) or $_FILES['newfile']['size'] == 0)) {
notify( $txt->uploadproblem );
}
else {
$importfile = $_FILES['newfile']['tmp_name'];
$fileisgood = true; $fileisgood = true;
} }
} }
@ -154,133 +109,52 @@
// process if we are happy file is ok // process if we are happy file is ok
if ($fileisgood) { if ($fileisgood) {
if (! is_readable("format/$format/format.php")) { if (! is_readable("format/$form->format/format.php")) {
error( get_string('formatnotfound','quiz', $format) ); error(get_string('formatnotfound','quiz', $form->format));
} }
require("format.php"); // Parent class require_once("format.php"); // Parent class
require("format/$format/format.php"); require_once("format/$form->format/format.php");
$classname = "qformat_$format"; $classname = "qformat_$form->format";
$qformat = new $classname(); $qformat = new $classname();
// load data into class // load data into class
$qformat->setCategory( $category ); $qformat->setCategory($category);
$qformat->setCourse( $course ); $qformat->setContexts($contexts->having_one_edit_tab_cap('import'));
$qformat->setFilename( $importfile ); $qformat->setCourse($COURSE);
$qformat->setMatchgrades( $params->matchgrades ); $qformat->setFilename($importfile);
$qformat->setCatfromfile( $catfromfile ); $qformat->setMatchgrades($form->matchgrades);
$qformat->setStoponerror( $params->stoponerror ); $qformat->setCatfromfile(!empty($form->catfromfile));
$qformat->setContextfromfile(!empty($form->contextfromfile));
$qformat->setStoponerror($form->stoponerror);
// Do anything before that we need to // Do anything before that we need to
if (! $qformat->importpreprocess()) { if (! $qformat->importpreprocess()) {
error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id))); error($txt->importerror, $thispageurl->out());
} }
// Process the uploaded file // Process the uploaded file
if (! $qformat->importprocess() ) { if (! $qformat->importprocess()) {
error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id))); error($txt->importerror, $thispageurl->out());
} }
// In case anything needs to be done after // In case anything needs to be done after
if (! $qformat->importpostprocess()) { if (! $qformat->importpostprocess()) {
error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id))); error($txt->importerror, $thispageurl->out());
} }
echo "<hr />"; echo "<hr />";
print_continue("edit.php?".$thispageurl->get_query_string()); print_continue("edit.php?".($thispageurl->get_query_string(array('category'=>"{$qformat->category->id},{$qformat->category->contextid}"))));
print_footer($course); print_footer($COURSE);
exit; exit;
} }
} }
/// Print upload form
// get list of available import formats
$fileformatnames = get_import_export_formats( 'import' );
print_heading_with_help($txt->importquestions, "import", "quiz"); print_heading_with_help($txt->importquestions, "import", "quiz");
/// Get all the existing categories now /// Print upload form
$catmenu = question_category_options($course->id, false, true); $import_form->display();
print_footer($COURSE);
//==========
// DISPLAY
//==========
?>
<form id="form" enctype="multipart/form-data" method="post" action="import.php">
<fieldset class="invisiblefieldset" style="display: block;">
<input type="hidden" name="sesskey" value="<?php echo sesskey(); ?>" />
<input type="hidden" name="courseid" value="<?php echo $localcourseid ?>" />
<?php echo $thispageurl->hidden_params_out(array(), 3); ?>
<?php print_simple_box_start("center"); ?>
<table cellpadding="5">
<tr>
<td align="right"><?php echo $txt->category; ?>:</td>
<td><?php choose_from_menu($catmenu, "category", $category->id, ""); ?>
<?php echo $txt->fromfile; ?>
<input name="catfromfile" type="checkbox" />
<?php helpbutton('importcategory', $txt->importcategory, 'quiz'); ?></td>
</tr>
<tr>
<td align="right"><?php echo $txt->fileformat; ?>:</td>
<td><?php choose_from_menu($fileformatnames, 'format', 'gift', '');
helpbutton("import", $txt->importquestions, 'quiz'); ?></td>
</tr>
<tr>
<td align="right"><?php echo $txt->matchgrades; ?></td>
<td><?php choose_from_menu($matchgrades,'matchgrades',$txt->matchgradeserror,'' );
helpbutton('matchgrades', $txt->matchgrades, 'quiz'); ?></td>
</tr>
<tr>
<td align="right"><?php echo $txt->stoponerror; ?></td>
<td><input name="stoponerror" type="checkbox" checked="checked" />
<?php helpbutton('stoponerror', $txt->stoponerror, 'quiz'); ?></td>
</tr>
</table>
<?php
print_simple_box_end();
print_simple_box_start('center'); ?>
<?php echo $txt->importfileupload; ?>
<table cellpadding="5">
<tr>
<td align="right"><?php echo $txt->upload; ?>:</td>
<td><?php upload_print_form_fragment(1,array('newfile'),null,false,null,$course->maxbytes,0,false); ?></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="submit" name="save" value="<?php echo $txt->uploadthisfile; ?>" /></td>
</tr>
</table>
<?php
print_simple_box_end();
print_simple_box_start('center'); ?>
<?php echo $txt->importfilearea; ?>
<table cellpadding="5">
<tr>
<td align="right"><?php echo $txt->file; ?>:</td>
<td><input type="text" name="choosefile" size="50" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><?php button_to_popup_window ("/files/index.php?id={$course->id}&amp;choose=form.choosefile",
"coursefiles", $txt->choosefile, 500, 750, $txt->choosefile); ?>
<input type="submit" name="save" value="<?php echo $txt->importfromthisfile; ?>" /></td>
</tr>
</table>
<?php
print_simple_box_end(); ?>
</fieldset>
</form>
<?php
print_footer($course);
?> ?>

View file

@ -38,12 +38,7 @@
$continue = false; $continue = false;
} }
require_login();
// this might break things in the future
if (!isteacherinanycourse()) {
error('This page is for teachers only');
}
if (!$continue) { if (!$continue) {
// Start a new attempt; delete the old session // Start a new attempt; delete the old session
@ -57,20 +52,23 @@
$url .= '&amp;continue=1'; $url .= '&amp;continue=1';
redirect($url); redirect($url);
} }
if (empty($quizid)) {
$quiz = new cmoptions;
$quiz->id = 0;
$quiz->review = $CFG->quiz_review;
} else if (!$quiz = get_record('quiz', 'id', $quizid)) {
error("Quiz id $quizid does not exist");
}
// Load the question information // Load the question information
if (!$questions = get_records('question', 'id', $id)) { if (!$questions = get_records('question', 'id', $id)) {
error('Could not load question'); error('Could not load question');
} }
if (empty($quizid)) {
$quiz = new cmoptions;
$quiz->id = 0;
$quiz->review = $CFG->quiz_review;
require_login_in_context($questions[$id]->contextid);
} else if (!$quiz = get_record('quiz', 'id', $quizid)) {
error("Quiz id $quizid does not exist");
} else {
require_login($quiz->course, false, get_coursemodule_from_instance('quiz', $quizid, $quiz->course));
}
if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) { if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) {
$questions[$id]->maxgrade = $maxgrade; $questions[$id]->maxgrade = $maxgrade;
} else { } else {
@ -84,10 +82,12 @@
error("This question doesn't belong to a valid category!"); error("This question doesn't belong to a valid category!");
} }
if (!has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $category->course)) and !$category->publish) { if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category)){
error("You can't preview these questions!"); error("You can't preview these questions!");
} }
$quiz->course = $category->course; if (isset($COURSE)){
$quiz->course = $COURSE->id;
}
// Load the question type specific information // Load the question type specific information
if (!get_question_options($questions)) { if (!get_question_options($questions)) {

View file

@ -13,23 +13,43 @@ require_once(dirname(__FILE__) . '/editlib.php');
require_once($CFG->libdir . '/filelib.php'); require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/formslib.php'); require_once($CFG->libdir . '/formslib.php');
$returnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
// Read URL parameters telling us which question to edit. // Read URL parameters telling us which question to edit.
$id = optional_param('id', 0, PARAM_INT); // question id $id = optional_param('id', 0, PARAM_INT); // question id
$cmid = optional_param('cmid', 0, PARAM_INT);
$qtype = optional_param('qtype', '', PARAM_FILE); $qtype = optional_param('qtype', '', PARAM_FILE);
$categoryid = optional_param('category', 0, PARAM_INT); $categoryid = optional_param('category', 0, PARAM_INT);
$cmid = optional_param('cmid', 0, PARAM_INT);
$courseid = optional_param('courseid', 0, PARAM_INT);
$wizardnow = optional_param('wizardnow', '', PARAM_ALPHA); $wizardnow = optional_param('wizardnow', '', PARAM_ALPHA);
$movecontext = optional_param('movecontext', 0, PARAM_BOOL);//switch to make question
//uneditable - form is displayed to edit question only
$returnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
if ($movecontext && !$id){
print_error('questiondoesnotexist', 'question', $returnurl);
}
if ($cmid){ if ($cmid){
list($module, $cm) = get_module_from_cmid($cmid); list($module, $cm) = get_module_from_cmid($cmid);
} else { require_login($cm->course, false, $cm);
$thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
} elseif ($courseid) {
require_login($courseid, false);
$thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
$module = null; $module = null;
$cm = null; $cm = null;
} else {
error('Need to pass courseid or cmid to this script.');
}
$contexts = new question_edit_contexts($thiscontext);
if (!$returnurl) {
$returnurl = "{$CFG->wwwroot}/question/edit.php?courseid={$COURSE->id}";
} }
// Validate the URL parameters.
if ($id) { if ($id) {
if (!$question = get_record('question', 'id', $id)) { if (!$question = get_record('question', 'id', $id)) {
print_error('questiondoesnotexist', 'question', $returnurl); print_error('questiondoesnotexist', 'question', $returnurl);
@ -47,79 +67,139 @@ if ($id) {
if (!$category = get_record('question_categories', 'id', $question->category)) { if (!$category = get_record('question_categories', 'id', $question->category)) {
print_error('categorydoesnotexist', 'question', $returnurl); print_error('categorydoesnotexist', 'question', $returnurl);
} }
if (!$returnurl) {
$returnurl = "{$CFG->wwwroot}/question/edit.php?courseid={$category->course}"; //permissions
$question->formoptions = new object();
$permissionstrs = array();
$categorycontext = get_context_instance_by_id($category->contextid);
$addpermission = has_capability('moodle/question:add', $categorycontext);
if ($id) {
$canview = question_has_capability_on($question, 'view');
if ($movecontext){
$question->formoptions->canedit = false;
$question->formoptions->canmove = (question_has_capability_on($question, 'move') && $contexts->have_cap('moodle/question:add'));
$question->formoptions->cansaveasnew = false;
$question->formoptions->repeatelements = false;
$question->formoptions->movecontext = true;
$formeditable = true;
question_require_capability_on($question, 'view');
} else {
$question->formoptions->canedit = question_has_capability_on($question, 'edit');
$question->formoptions->canmove = (question_has_capability_on($question, 'move') && $addpermission);
$question->formoptions->cansaveasnew = (($canview ||question_has_capability_on($question, 'edit')) && $addpermission);
$question->formoptions->repeatelements = ($question->formoptions->canedit || $question->formoptions->cansaveasnew);
$formeditable = $question->formoptions->canedit || $question->formoptions->cansaveasnew || $question->formoptions->canmove;
$question->formoptions->movecontext = false;
if (!$formeditable){
question_require_capability_on($question, 'view');
}
}
} else { // creating a new question
require_capability('moodle/question:add', $categorycontext);
$formeditable = true;
$question->formoptions->repeatelements = true;
$question->formoptions->movecontext = false;
} }
$question->category = "$category->id,$category->contextid";
if ($formeditable && $id){
$question->categorymoveto = $question->category;
}
// Validate the question type. // Validate the question type.
if (!isset($QTYPES[$question->qtype])) { if (!isset($QTYPES[$question->qtype])) {
print_error('unknownquestiontype', 'question', $returnurl, $question->qtype); print_error('unknownquestiontype', 'question', $returnurl, $question->qtype);
} }
$CFG->pagepath = 'question/type/' . $question->qtype; $CFG->pagepath = 'question/type/' . $question->qtype;
// Check the user is logged in and has enough premissions.
require_login($category->course, false);
$coursecontext = get_context_instance(CONTEXT_COURSE, $category->course);
require_capability('moodle/question:manage', $coursecontext);
// Create the question editing form. // Create the question editing form.
if ($wizardnow!==''){ if ($wizardnow!=='' && !$movecontext){
if (!method_exists($QTYPES[$question->qtype], 'next_wizard_form')){ if (!method_exists($QTYPES[$question->qtype], 'next_wizard_form')){
print_error('missingimportantcode', 'question', $returnurl, 'wizard form definition'); print_error('missingimportantcode', 'question', $returnurl, 'wizard form definition');
} else { } else {
$mform = $QTYPES[$question->qtype]->next_wizard_form('question.php', $question, $wizardnow); $mform = $QTYPES[$question->qtype]->next_wizard_form('question.php', $question, $wizardnow, $formeditable);
} }
} else { } else {
$mform = $QTYPES[$question->qtype]->create_editing_form('question.php', $question); $mform = $QTYPES[$question->qtype]->create_editing_form('question.php', $question, $category, $contexts, $formeditable);
} }
if ($mform === null) { if ($mform === null) {
print_error('missingimportantcode', 'question', $returnurl, 'question editing form definition for "'.$question->qtype.'"'); print_error('missingimportantcode', 'question', $returnurl, 'question editing form definition for "'.$question->qtype.'"');
} }
$toform = $question; // send the question object and a few more parameters to the form $toform = $question; // send the question object and a few more parameters to the form
$toform->returnurl = $returnurl; $toform->returnurl = $returnurl;
$toform->movecontext = $movecontext;
if ($cm !== null){ if ($cm !== null){
$toform->cmid = $cm->id; $toform->cmid = $cm->id;
$toform->courseid = $cm->course;
} else {
$toform->courseid = $COURSE->id;
} }
$mform->set_data($toform); $mform->set_data($toform);
if ($mform->is_cancelled()){ if ($mform->is_cancelled()){
redirect($returnurl); redirect($returnurl);
} elseif ($data = $mform->get_data()){ } elseif ($data = $mform->get_data()){
$returnurl = new moodle_url($returnurl);
//select category that question has been saved in / moved to when we return to question bank
if (!empty($data->categorymoveto)){
$returnurl->param('category', $data->categorymoveto);
} else if (!empty($data->category)){
$returnurl->param('category', $data->category);
}
$returnurl = $returnurl->out();
if (!empty($data->makecopy)) { if (!empty($data->makecopy)) {
$question->id = 0; // causes a new question to be created. $question->id = 0; // causes a new question to be created.
$question->hidden = 0; // Copies should not be hidden $question->hidden = 0; // Copies should not be hidden
} }
if ($movecontext){
list($tocatid, $tocontextid) = explode(',', $data->categorymoveto);
$tocontext = get_context_instance_by_id($tocontextid);
require_capability('moodle/question:add', $tocontext);
if (get_filesdir_from_context($categorycontext) != get_filesdir_from_context($tocontext)){
$movecontexturl = new moodle_url($CFG->wwwroot.'/question/contextmoveq.php',
array('returnurl' => $returnurl,
'ids'=>$question->id,
'tocatid'=> $tocatid));
if ($cmid){
$movecontexturl->param('cmid', $cmid);
} else {
$movecontexturl->param('courseid', $COURSE->id);
}
redirect($movecontexturl->out());
}
}
$question = $QTYPES[$question->qtype]->save_question($question, $data, $COURSE, $wizardnow); $question = $QTYPES[$question->qtype]->save_question($question, $data, $COURSE, $wizardnow);
if ($QTYPES[$qtype]->finished_edit_wizard($data)){ if ($QTYPES[$qtype]->finished_edit_wizard($data) || $movecontext){
if (optional_param('inpopup', 0, PARAM_BOOL)) { if (optional_param('inpopup', 0, PARAM_BOOL)) {
notify(get_string('changessaved'), ''); notify(get_string('changessaved'), '');
close_window(3); close_window(3);
} else { } else {
redirect($returnurl); redirect($returnurl);
} }
die;
} else { } else {
//useful for passing data to the next page which is not saved in the database $nexturlparams = array('returnurl'=>$returnurl)
$queryappend = ''; + $data->nextpageparam;//useful for passing data to the next page which is not saved in the database
if (isset($data->nextpageparam)){
foreach ($data->nextpageparam as $key => $param){
$queryappend .= "&".urlencode($key).'='.urlencode($param);
}
}
if ($question->id) { if ($question->id) {
$nexturl = "question.php?id=$question->id&returnurl=" . urlencode($returnurl); $nexturlparams['id'] = $question->id;
} else { // only for creating new questions } else { // only for creating new questions
$nexturl = "question.php?category=$question->category&qtype=$question->qtype&returnurl=".urlencode($returnurl); $nexturlparams['category'] = $question->category;
$nexturlparams['qtype'] =$question->qtype;
} }
redirect($nexturl.'&wizardnow='.$data->wizard.$queryappend, '', 20); $nexturlparams['wizardnow'] = $data->wizard;
$nexturl = new moodle_url('question.php', $nexturlparams);
redirect($nexturl);
} }
} else { } else {
list($streditingquestion,) = $QTYPES[$question->qtype]->get_heading(); list($streditingquestion,) = $QTYPES[$question->qtype]->get_heading();
if ($cm !== null) { if ($cm !== null) {
$strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $category->course)) $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $COURSE->id))
? update_module_button($cm->id, $category->course, get_string('modulename', $cm->modname)) ? update_module_button($cm->id, $cm->course, get_string('modulename', $cm->modname))
: ""; : "";
$navlinks = array(); $navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$category->course", 'type' => 'activity'); $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$category->course", 'type' => 'activity');
@ -139,10 +219,10 @@ if ($mform->is_cancelled()){
print_header_simple($streditingquestion, '', $navigation); print_header_simple($streditingquestion, '', $navigation);
} }
// Display a heading, question editing form and possibly some extra content needed for // Display a heading, question editing form and possibly some extra content needed for
// for this question type. // for this question type.
$QTYPES[$question->qtype]->display_question_editing_page($mform, $question, $wizardnow); $QTYPES[$question->qtype]->display_question_editing_page($mform, $question, $wizardnow);
print_footer($COURSE); print_footer($COURSE);
} }
?> ?>

View file

@ -85,20 +85,61 @@
include_once($CFG->libdir.'/questionlib.php'); include_once($CFG->libdir.'/questionlib.php');
function restore_question_categories($category,$restore) { /**
* Returns the best question category (id) found to restore one
global $CFG; * question category from a backup file. Works by stamp.
*
$status = true; * @param object $restore preferences for restoration
* @param array $contextinfo fragment of decoded xml
//Hook to call Moodle < 1.5 Quiz Restore * @return object best context instance for this category to be in
if ($restore->backup_version < 2005043000) { */
include_once($CFG->dirroot.'/mod/quiz/restorelibpre15.php'); function restore_question_get_best_category_context($restore, $contextinfo) {
return quiz_restore_pre15_question_categories($category,$restore); switch ($contextinfo['LEVEL'][0]['#']) {
case 'module':
$instanceinfo = backup_getid($restore->backup_unique_code, 'course_modules', $contextinfo['INSTANCE'][0]['#']);
$tocontext = get_context_instance(CONTEXT_MODULE, $instanceinfo->new_id);
break;
case 'course':
$tocontext = get_context_instance(CONTEXT_COURSE, $restore->course_id);
break;
case 'coursecategory':
//search COURSECATEGORYLEVEL steps up the course cat tree or
//to the top of the tree if steps are exhausted.
$catno = $contextinfo['COURSECATEGORYLEVEL'][0]['#'];
$catid = get_field('course', 'parent', 'id', $restore->course_id);
while ($catno > 1){
$nextcatid = get_field('course_categories', 'parent', 'id', $catid);
if ($nextcatid == 0){
break;
}
$catid == $nextcatid;
$catno--;
}
$tocontext = get_context_instance(CONTEXT_COURSECAT, $catid);
break;
case 'system':
$tocontext = get_context_instance(CONTEXT_SYSTEM);
break;
}
return $tocontext;
} }
function restore_question_categories($info, $restore) {
$status = true;
//Iterate over each category
foreach ($info as $category) {
$status = $status && restore_question_category($category, $restore);
}
$status = $status && restore_recode_category_parents($restore);
return $status;
}
function restore_question_category($category, $restore){
$status = true;
//Skip empty categories (some backups can contain them)
if (!empty($category->id)) {
//Get record from backup_ids //Get record from backup_ids
$data = backup_getid($restore->backup_unique_code,"question_categories",$category->id); $data = backup_getid($restore->backup_unique_code, "question_categories", $category->id);
if ($data) { if ($data) {
//Now get completed xmlized object //Now get completed xmlized object
@ -109,44 +150,66 @@
//Now, build the question_categories record structure //Now, build the question_categories record structure
$question_cat = new stdClass; $question_cat = new stdClass;
$question_cat->course = $restore->course_id;
$question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['0']['#']); $question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['0']['#']);
$question_cat->info = backup_todb($info['QUESTION_CATEGORY']['#']['INFO']['0']['#']); $question_cat->info = backup_todb($info['QUESTION_CATEGORY']['#']['INFO']['0']['#']);
$question_cat->publish = backup_todb($info['QUESTION_CATEGORY']['#']['PUBLISH']['0']['#']);
$question_cat->stamp = backup_todb($info['QUESTION_CATEGORY']['#']['STAMP']['0']['#']); $question_cat->stamp = backup_todb($info['QUESTION_CATEGORY']['#']['STAMP']['0']['#']);
//parent is fixed after all categories are restored and we know all the new ids.
$question_cat->parent = backup_todb($info['QUESTION_CATEGORY']['#']['PARENT']['0']['#']); $question_cat->parent = backup_todb($info['QUESTION_CATEGORY']['#']['PARENT']['0']['#']);
$question_cat->sortorder = backup_todb($info['QUESTION_CATEGORY']['#']['SORTORDER']['0']['#']); $question_cat->sortorder = backup_todb($info['QUESTION_CATEGORY']['#']['SORTORDER']['0']['#']);
if ($catfound = restore_get_best_question_category($question_cat, $restore->course_id)) {
$newid = $catfound;
} else {
if (!$question_cat->stamp) { if (!$question_cat->stamp) {
$question_cat->stamp = make_unique_id_code(); $question_cat->stamp = make_unique_id_code();
} }
$newid = insert_record ("question_categories",$question_cat); if (isset($info['QUESTION_CATEGORY']['#']['PUBLISH'])) {
$course = $restore->course_id;
$publish = backup_todb($info['QUESTION_CATEGORY']['#']['PUBLISH']['0']['#']);
if ($publish){
$tocontext = get_context_instance(CONTEXT_SYSTEM);
} else {
$tocontext = get_context_instance(CONTEXT_COURSE, $course);
}
} else {
$tocontext = restore_question_get_best_category_context($restore, $info['QUESTION_CATEGORY']['#']['CONTEXT']['0']['#']);
}
$question_cat->contextid = $tocontext->id;
//does cat exist ?? if it does we check if the cat and questions already exist whether we have
//add permission or not if we have no permission to add questions to SYSTEM or COURSECAT context
//AND the question does not already exist then we create questions in COURSE context.
if (!$fcat = get_record('question_categories','contextid', $question_cat->contextid, 'stamp', $question_cat->stamp)){
//no preexisting cat
if ((($tocontext->contextlevel == CONTEXT_SYSTEM) || ($tocontext->contextlevel == CONTEXT_COURSECAT))
&& !has_capability('moodle/question:add', $tocontext)){
//no preexisting cat and no permission to create questions here
//must restore to course.
$tocontext = get_context_instance(CONTEXT_COURSE, $restore->course_id);
}
$question_cat->contextid = $tocontext->id;
if (!$fcat = get_record('question_categories','contextid', $question_cat->contextid, 'stamp', $question_cat->stamp)){
$question_cat->id = insert_record ("question_categories", $question_cat);
} else {
$question_cat = $fcat;
}
//we'll be restoring all questions here.
backup_putid($restore->backup_unique_code, "question_categories", $category->id, $question_cat->id);
} else {
$question_cat = $fcat;
//we found an existing best category
//but later if context is above course need to check if there are questions need creating in category
//if we do need to create questions and permissions don't allow it create new category in course
} }
//Do some output //Do some output
if ($newid) {
if (!defined('RESTORE_SILENTLY')) { if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string('category', 'quiz')." \"".$question_cat->name."\"<br />"; echo "<li>".get_string('category', 'quiz')." \"".$question_cat->name."\"<br />";
} }
} else {
//We must never arrive here !!
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string('category', 'quiz')." \"".$question_cat->name."\" Error!<br />";
}
$status = false;
}
backup_flush(300); backup_flush(300);
//Here category has been created or selected, so save results in backup_ids and start with questions //start with questions
if ($newid and $status) { if ($question_cat->id) {
//We have the newid, update backup_ids //We have the newid, update backup_ids
backup_putid($restore->backup_unique_code,"question_categories",
$category->id, $newid);
//Now restore question //Now restore question
$status = restore_questions ($category->id, $newid,$info,$restore); $status = restore_questions($category->id, $question_cat, $info, $restore);
} else { } else {
$status = false; $status = false;
} }
@ -156,11 +219,76 @@
} else { } else {
echo 'Could not get backup info for question category'. $category->id; echo 'Could not get backup info for question category'. $category->id;
} }
}
return $status; return $status;
} }
function restore_questions ($old_category_id,$new_category_id,$info,$restore) { function restore_recode_category_parents($restore){
global $CFG;
$status = true;
//Now we have to recode the parent field of each restored category
$categories = get_records_sql("SELECT old_id, new_id
FROM {$CFG->prefix}backup_ids
WHERE backup_code = $restore->backup_unique_code AND
table_name = 'question_categories'");
if ($categories) {
//recode all parents to point at their old parent cats no matter what context the parent is now in
foreach ($categories as $category) {
$restoredcategory = get_record('question_categories','id',$category->new_id);
if ($restoredcategory->parent != 0) {
$updateobj = new object();
$updateobj->id = $restoredcategory->id;
$idcat = backup_getid($restore->backup_unique_code,'question_categories',$restoredcategory->parent);
if ($idcat->new_id) {
$updateobj->parent = $idcat->new_id;
} else {
$updateobj->parent = 0;
}
$status = $status && update_record('question_categories', $updateobj);
}
}
//now we have recoded all parents, check through all parents and set parent to be
//grand parent / great grandparent etc where there is one in same context
//or else set parent to 0 (top level category).
$toupdate = array();
foreach ($categories as $category) {
$restoredcategory = get_record('question_categories','id',$category->new_id);
if ($restoredcategory->parent != 0) {
$nextparentid = $restoredcategory->parent;
do {
if (!$parent = get_record('question_categories', 'id', $nextparentid)){
if (!defined('RESTORE_SILENTLY')) {
echo 'Could not find parent for question category '. $category->id.' recoding as top category item.<br />';
}
break;//record fetch failed finish loop
} else {
$nextparentid = $nextparent->parent;
}
} while (($nextparentid != 0) && ($parent->contextid != $restoredcategory->contextid));
if (!$parent || ($parent->id != $restoredcategory->parent)){
//change needs to be made to the parent field.
if ($parent && ($parent->contextid == $restoredcategory->contextid)){
$toupdate[$restoredcategory->id] = $parent->id;
} else {
//searched up the tree till we came to the top and did not find cat in same
//context or there was an error getting next parent record
$toupdate[$restoredcategory->id] = 0;
}
}
}
}
//now finally do the changes to parent field.
foreach ($toupdate as $id => $parent){
$updateobj = new object();
$updateobj->id = $id;
$updateobj->parent = $parent;
$status = $status && update_record('question_categories', $updateobj);
}
}
return $status;
}
function restore_questions ($old_category_id, $best_question_cat, $info, $restore) {
global $CFG, $QTYPES; global $CFG, $QTYPES;
@ -186,7 +314,6 @@
//Now, build the question record structure //Now, build the question record structure
$question = new object; $question = new object;
$question->category = $new_category_id;
$question->parent = backup_todb($que_info['#']['PARENT']['0']['#']); $question->parent = backup_todb($que_info['#']['PARENT']['0']['#']);
$question->name = backup_todb($que_info['#']['NAME']['0']['#']); $question->name = backup_todb($que_info['#']['NAME']['0']['#']);
$question->questiontext = backup_todb($que_info['#']['QUESTIONTEXT']['0']['#']); $question->questiontext = backup_todb($que_info['#']['QUESTIONTEXT']['0']['#']);
@ -204,6 +331,10 @@
$question->stamp = backup_todb($que_info['#']['STAMP']['0']['#']); $question->stamp = backup_todb($que_info['#']['STAMP']['0']['#']);
$question->version = backup_todb($que_info['#']['VERSION']['0']['#']); $question->version = backup_todb($que_info['#']['VERSION']['0']['#']);
$question->hidden = backup_todb($que_info['#']['HIDDEN']['0']['#']); $question->hidden = backup_todb($que_info['#']['HIDDEN']['0']['#']);
$question->timecreated = backup_todb($que_info['#']['TIMECREATED']['0']['#']);
$question->timemodified = backup_todb($que_info['#']['TIMEMODIFIED']['0']['#']);
$question->createdby = backup_todb($que_info['#']['CREATEDBY']['0']['#']);
$question->modifiedby = backup_todb($que_info['#']['MODIFIEDBY']['0']['#']);
if ($restore->backup_version < 2006032200) { if ($restore->backup_version < 2006032200) {
// The qtype was an integer that now needs to be converted to the name // The qtype was an integer that now needs to be converted to the name
@ -213,36 +344,65 @@
$question->qtype = $qtypenames[$question->qtype]; $question->qtype = $qtypenames[$question->qtype];
} }
//Check if the question exists //Check if the question exists by category, stamp, and version
//by category, stamp, and version //first check for the question in the context specified in backup
$question_exists = get_record ("question","category",$question->category, $existingquestion = get_record ("question", "category", $best_question_cat->id, "stamp", $question->stamp,"version",$question->version);
"stamp",$question->stamp,"version",$question->version);
//If the question exists, only record its id //If the question exists, only record its id
if ($question_exists) { //always use existing question, no permissions check here
$newid = $question_exists->id; if ($existingquestion) {
$question = $existingquestion;
$creatingnewquestion = false; $creatingnewquestion = false;
//Else, create a new question
} else { } else {
//then if context above course level check permissions and if no permission
//to restore above course level then restore to cat in course context.
$bestcontext = get_context_instance_by_id($best_question_cat->contextid);
if (($bestcontext->contextlevel == CONTEXT_SYSTEM || $bestcontext->contextlevel == CONTEXT_COURSECAT)
&& !has_capability('moodle/question:add', $bestcontext)){
if (!isset($course_question_cat)) {
$coursecontext = get_context_instance(CONTEXT_COURSE, $restore->course_id);
$course_question_cat = clone($best_question_cat);
$course_question_cat->contextid = $coursecontext->id;
//create cat if it doesn't exist
if (!$fcat = get_record('question_categories','contextid', $course_question_cat->contextid, 'stamp', $course_question_cat->stamp)){
$course_question_cat->id = insert_record ("question_categories", $course_question_cat);
backup_putid($restore->backup_unique_code, "question_categories", $old_category_id, $course_question_cat->id);
} else {
$course_question_cat = $fcat;
}
//will fix category parents after all questions and categories restored. Will set parent to 0 if
//no parent in same context.
}
$question->category = $course_question_cat->id;
//does question already exist in course cat
$existingquestion = get_record ("question", "category", $question->category, "stamp", $question->stamp, "version", $question->version);
} else {
//permissions ok, restore to best cat
$question->category = $best_question_cat->id;
}
if (!$existingquestion){
//The structure is equal to the db, so insert the question //The structure is equal to the db, so insert the question
$newid = insert_record ("question",$question); $question->id = insert_record ("question", $question);
$creatingnewquestion = true; $creatingnewquestion = true;
} else {
$question = $existingquestion;
$creatingnewquestion = false;
}
} }
//Save newid to backup tables //Save newid to backup tables
if ($newid) { if ($question->id) {
//We have the newid, update backup_ids //We have the newid, update backup_ids
backup_putid($restore->backup_unique_code,"question",$oldid, backup_putid($restore->backup_unique_code, "question", $oldid, $question->id);
$newid);
} }
$restored_questions[$i] = new stdClass; $restored_questions[$i] = new stdClass;
$restored_questions[$i]->newid = $newid; $restored_questions[$i]->newid = $question->id;
$restored_questions[$i]->oldid = $oldid; $restored_questions[$i]->oldid = $oldid;
$restored_questions[$i]->qtype = $question->qtype; $restored_questions[$i]->qtype = $question->qtype;
$restored_questions[$i]->parent = $question->parent; $restored_questions[$i]->parent = $question->parent;
$restored_questions[$i]->is_new = $creatingnewquestion; $restored_questions[$i]->is_new = $creatingnewquestion;
} }
backup_flush(300);
// Loop again, now all the question id mappings exist, so everything can // Loop again, now all the question id mappings exist, so everything can
// be restored. // be restored.
@ -757,12 +917,23 @@
} }
$extraprocessing = array(); $extraprocessing = array();
$coursemodulecontexts = array();
$context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
$coursemodulecontexts[] = $context->id;
$cms = get_records('course_modules', 'course', $restore->course_id, '', 'id');
if ($cms){
foreach ($cms as $cm){
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$coursemodulecontexts[] = $context->id;
}
}
$coursemodulecontextslist = join($coursemodulecontexts, ',');
// Decode links in questions. // Decode links in questions.
if ($questions = get_records_sql('SELECT q.id, q.qtype, q.questiontext, q.generalfeedback if ($questions = get_records_sql('SELECT q.id, q.qtype, q.questiontext, q.generalfeedback '.
FROM ' . $CFG->prefix . 'question q, 'FROM ' . $CFG->prefix . 'question q, '.
' . $CFG->prefix . 'question_categories qc $CFG->prefix . 'question_categories qc '.
WHERE q.category = qc.id 'WHERE q.category = qc.id '.
AND qc.course = ' . $restore->course_id)) { 'AND qc.contextid IN (' .$coursemodulecontextslist.')')) {
foreach ($questions as $question) { foreach ($questions as $question) {
$questiontext = restore_decode_content_links_worker($question->questiontext, $restore); $questiontext = restore_decode_content_links_worker($question->questiontext, $restore);
@ -800,8 +971,8 @@
' . $CFG->prefix . 'question q, ' . $CFG->prefix . 'question q,
' . $CFG->prefix . 'question_categories qc ' . $CFG->prefix . 'question_categories qc
WHERE qa.question = q.id WHERE qa.question = q.id
AND q.category = qc.id AND q.category = qc.id '.
AND qc.course = ' . $restore->course_id)) { 'AND qc.contextid IN ('.$coursemodulecontextslist.')')) {
foreach ($answers as $answer) { foreach ($answers as $answer) {
$feedback = restore_decode_content_links_worker($answer->feedback, $restore); $feedback = restore_decode_content_links_worker($answer->feedback, $restore);

View file

@ -11,16 +11,17 @@
if (!isset($currenttab)) { if (!isset($currenttab)) {
$currenttab = ''; $currenttab = '';
} }
if (!isset($course)) { if (!isset($COURSE)) {
error('No course specified'); error('No course specified');
} }
$tabs = array(); $tabs = array();
$inactive = array(); $inactive = array();
$row = array(); $row = array();
questionbank_navigation_tabs($row, $context, 'courseid='.$course->id); questionbank_navigation_tabs($row, $contexts, 'courseid='.$COURSE->id);
$tabs[] = $row; $tabs[] = $row;
print_tabs($tabs, $currenttab, array()); print_tabs($tabs, $currenttab, array());
?> ?>

View file

@ -59,7 +59,8 @@ class question_edit_calculated_form extends question_edit_form {
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz')); $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$repeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats); $repeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz')); $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
$repeatedoptions['feedback']['type'] = PARAM_RAW; $repeatedoptions['feedback']['type'] = PARAM_RAW;
if (isset($this->question->options)){ if (isset($this->question->options)){
@ -67,7 +68,11 @@ class question_edit_calculated_form extends question_edit_form {
} else { } else {
$count = 0; $count = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = $count + 1; $repeatsatstart = $count + 1;
} else {
$repeatsatstart = $count;
}
$this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 1, get_string('addmoreanswerblanks', 'qtype_calculated')); $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 1, get_string('addmoreanswerblanks', 'qtype_calculated'));
$repeated = array(); $repeated = array();
@ -84,7 +89,11 @@ class question_edit_calculated_form extends question_edit_form {
} else { } else {
$countunits = 0; $countunits = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = $countunits + 1; $repeatsatstart = $countunits + 1;
} else {
$repeatsatstart = $countunits;
}
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_calculated', '{no}')); $this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_calculated', '{no}'));
$firstunit =& $mform->getElement('multiplier[0]'); $firstunit =& $mform->getElement('multiplier[0]');
@ -158,7 +167,7 @@ class question_edit_calculated_form extends question_edit_form {
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
//verifying for errors in {=...} in question text; //verifying for errors in {=...} in question text;
$qtext = ""; $qtext = "";
$qtextremaining = $data['questiontext'] ; $qtextremaining = $data['questiontext'] ;

View file

@ -306,6 +306,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
} }
break; break;
case 'datasetdefinitions': case 'datasetdefinitions':
$this->save_dataset_definitions($form); $this->save_dataset_definitions($form);
break; break;
case 'datasetitems': case 'datasetitems':

View file

@ -101,6 +101,15 @@ class question_dataset_dependent_definitions_form extends moodleform {
$mform->addElement('hidden', 'category'); $mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_INT); $mform->setType('category', PARAM_INT);
$mform->addElement('hidden', 'id'); $mform->addElement('hidden', 'id');
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->setDefault('courseid', 0);
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0);
$mform->setType('id', PARAM_INT); $mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetitems'); $mform->addElement('hidden', 'wizard', 'datasetitems');
$mform->setType('wizard', PARAM_ALPHA); $mform->setType('wizard', PARAM_ALPHA);

View file

@ -156,6 +156,15 @@ class question_dataset_dependent_items_form extends moodleform {
$mform->setType('qtype', PARAM_ALPHA); $mform->setType('qtype', PARAM_ALPHA);
$mform->addElement('hidden', 'category'); $mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_INT); $mform->setType('category', PARAM_INT);
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->setDefault('courseid', 0);
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0);
$mform->addElement('hidden', 'id'); $mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT); $mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetitems'); $mform->addElement('hidden', 'wizard', 'datasetitems');

View file

@ -46,7 +46,7 @@ class description_qtype extends default_questiontype {
// For editing teachers print a link to an editing popup window // For editing teachers print a link to an editing popup window
$editlink = ''; $editlink = '';
if (has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $cmoptions->course))) { if (question_has_capability_on($question, 'edit')) {
$stredit = get_string('edit'); $stredit = get_string('edit');
$linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />'; $linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />';
$editlink = link_to_popup_window('/question/question.php?id='.$question->id, $stredit, $linktext, 450, 550, $stredit, '', true); $editlink = link_to_popup_window('/question/question.php?id='.$question->id, $stredit, $linktext, 450, 550, $stredit, '', true);

View file

@ -28,9 +28,25 @@ class question_edit_form extends moodleform {
*/ */
var $question; var $question;
function question_edit_form($submiturl, $question){ var $contexts;
var $category;
var $categorycontext;
var $coursefilesid;
function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
$this->question = $question; $this->question = $question;
parent::moodleform($submiturl);
$this->contexts = $contexts;
$this->category = $category;
$this->categorycontext = get_context_instance_by_id($category->contextid);
//course id or site id depending on question cat context
$this->coursefilesid = get_filesdir_from_context(get_context_instance_by_id($category->contextid));
parent::moodleform($submiturl, null, 'post', '', null, $formeditable);
} }
/** /**
@ -51,22 +67,53 @@ class question_edit_form extends moodleform {
// Standard fields at the start of the form. // Standard fields at the start of the form.
$mform->addElement('header', 'generalheader', get_string("general", 'form')); $mform->addElement('header', 'generalheader', get_string("general", 'form'));
$mform->addElement('questioncategory', 'category', get_string('category', 'quiz'), null, if (!isset($this->question->id)){
array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true)); //adding question
$mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
array('contexts' => array($this->categorycontext)));
} elseif (!($this->question->formoptions->canmove || $this->question->formoptions->cansaveasnew)){
//editing question with no permission to move from category.
$mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
array('contexts' => array($this->categorycontext)));
} elseif ($this->question->formoptions->movecontext){
//moving question to another context.
$mform->addElement('questioncategory', 'categorymoveto', get_string('category', 'quiz'),
array('contexts' => $this->contexts->having_cap('moodle/question:add')));
$mform->addElement('text', 'name', get_string('questionname', 'quiz'), } else {
array('size' => 50)); //editing question with permission to move from category or save as new q
$currentgrp = array();
$currentgrp[0] =& $mform->createElement('questioncategory', 'category', get_string('categorycurrent', 'question'),
array('contexts' => array($this->categorycontext)));
if ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew){
//not move only form
$currentgrp[1] =& $mform->createElement('checkbox', 'usecurrentcat', '', get_string('categorycurrentuse', 'question'));
$mform->setDefault('usecurrentcat', 1);
}
$currentgrp[0]->freeze();
$currentgrp[0]->setPersistantFreeze(false);
$mform->addGroup($currentgrp, 'currentgrp', get_string('categorycurrent', 'question'), null, false);
$mform->addElement('questioncategory', 'categorymoveto', get_string('categorymoveto', 'question'),
array('contexts' => array($this->categorycontext)));
if ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew){
//not move only form
$mform->disabledIf('categorymoveto', 'usecurrentcat', 'checked');
}
}
$mform->addElement('text', 'name', get_string('questionname', 'quiz'), array('size' => 50));
$mform->setType('name', PARAM_TEXT); $mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', null, 'required', null, 'client');
$mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'), $mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'),
array('rows' => 15, 'course' => $COURSE->id)); array('rows' => 15, 'course' => $this->coursefilesid));
$mform->setType('questiontext', PARAM_RAW); $mform->setType('questiontext', PARAM_RAW);
$mform->setHelpButton('questiontext', array(array('questiontext', get_string('questiontext', 'quiz'), 'quiz'), 'richtext'), false, 'editorhelpbutton'); $mform->setHelpButton('questiontext', array(array('questiontext', get_string('questiontext', 'quiz'), 'quiz'), 'richtext'), false, 'editorhelpbutton');
$mform->addElement('format', 'questiontextformat', get_string('format')); $mform->addElement('format', 'questiontextformat', get_string('format'));
make_upload_directory("$COURSE->id"); // Just in case make_upload_directory($this->coursefilesid); // Just in case
$coursefiles = get_directory_list("$CFG->dataroot/$COURSE->id", $CFG->moddata); $coursefiles = get_directory_list("$CFG->dataroot/$this->coursefilesid", $CFG->moddata);
foreach ($coursefiles as $filename) { foreach ($coursefiles as $filename) {
if (mimeinfo("icon", $filename) == "image.gif") { if (mimeinfo("icon", $filename) == "image.gif") {
$images["$filename"] = $filename; $images["$filename"] = $filename;
@ -92,13 +139,33 @@ class question_edit_form extends moodleform {
$mform->setDefault('penalty', 0.1); $mform->setDefault('penalty', 0.1);
$mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'), $mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $COURSE->id)); array('rows' => 10, 'course' => $this->coursefilesid));
$mform->setType('generalfeedback', PARAM_RAW); $mform->setType('generalfeedback', PARAM_RAW);
$mform->setHelpButton('generalfeedback', array('generalfeedback', get_string('generalfeedback', 'quiz'), 'quiz')); $mform->setHelpButton('generalfeedback', array('generalfeedback', get_string('generalfeedback', 'quiz'), 'quiz'));
// Any questiontype specific fields. // Any questiontype specific fields.
$this->definition_inner($mform); $this->definition_inner($mform);
if (!empty($this->question->id)){
$mform->addElement('header', 'createdmodifiedheader', get_string('createdmodifiedheader', 'question'));
$a = new object();
if (!empty($this->question->createdby)){
$a->time = userdate($this->question->timecreated);
$a->user = fullname(get_record('user', 'id', $this->question->createdby));
} else {
$a->time = get_string('unknown', 'question');
$a->user = get_string('unknown', 'question');
}
$mform->addElement('static', 'created', get_string('created', 'question'), get_string('byandon', 'question', $a));
if (!empty($this->question->modifiedby)){
$a = new object();
$a->time = userdate($this->question->timemodified);
$a->user = fullname(get_record('user', 'id', $this->question->modifiedby));
$mform->addElement('static', 'modified', get_string('modified', 'question'), get_string('byandon', 'question', $a));
}
}
// Standard fields at the end of the form. // Standard fields at the end of the form.
$mform->addElement('hidden', 'id'); $mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT); $mform->setType('id', PARAM_INT);
@ -112,22 +179,46 @@ class question_edit_form extends moodleform {
$mform->addElement('hidden', 'versioning'); $mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL); $mform->setType('versioning', PARAM_BOOL);
$mform->addElement('hidden', 'movecontext');
$mform->setType('movecontext', PARAM_BOOL);
$mform->addElement('hidden', 'cmid'); $mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT); $mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0); $mform->setDefault('cmid', 0);
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->setDefault('courseid', 0);
$mform->addElement('hidden', 'returnurl'); $mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL); $mform->setType('returnurl', PARAM_LOCALURL);
$mform->setDefault('returnurl', ''); $mform->setDefault('returnurl', 0);
$buttonarray = array(); $buttonarray = array();
if (!empty($this->question->id)){
//editing / moving question
if ($this->question->formoptions->movecontext){
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('moveq', 'question'));
} elseif ($this->question->formoptions->canedit || $this->question->formoptions->canmove ||$this->question->formoptions->movecontext){
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges')); $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
if (!empty($this->question->id)) { }
if ($this->question->formoptions->cansaveasnew){
$buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz')); $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
} }
$buttonarray[] = &$mform->createElement('cancel'); $buttonarray[] = &$mform->createElement('cancel');
} else {
// adding new question
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
$buttonarray[] = &$mform->createElement('cancel');
}
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar'); $mform->closeHeaderBefore('buttonar');
if ($this->question->formoptions->movecontext){
$mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar'));
} elseif ((!empty($this->question->id)) && (!($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew))){
$mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar', 'currentgrp'));
}
} }
/** /**
@ -167,6 +258,7 @@ class question_edit_form extends moodleform {
function qtype() { function qtype() {
return ''; return '';
} }
} }
?> ?>

View file

@ -19,7 +19,8 @@ class question_edit_essay_form extends question_edit_form {
* @param MoodleQuickForm $mform the form being built. * @param MoodleQuickForm $mform the form being built.
*/ */
function definition_inner(&$mform) { function definition_inner(&$mform) {
$mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz")); $mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"),
array('course' => $this->coursefilesid));
$mform->setType('feedback', PARAM_RAW); $mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0); $mform->addElement('hidden', 'fraction', 0);

View file

@ -36,8 +36,12 @@ class question_edit_match_form extends question_edit_form {
} else { } else {
$countsubquestions = 0; $countsubquestions = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))? $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD); QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
} else {
$repeatsatstart = $countsubquestions;
}
$mform->setType('subanswer', PARAM_TEXT); $mform->setType('subanswer', PARAM_TEXT);
$mform->setType('subquestion', PARAM_TEXT); $mform->setType('subquestion', PARAM_TEXT);
@ -67,7 +71,7 @@ class question_edit_match_form extends question_edit_form {
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
$answers = $data['subanswers']; $answers = $data['subanswers'];
$questions = $data['subquestions']; $questions = $data['subquestions'];
$questioncount = 0; $questioncount = 0;

View file

@ -646,6 +646,38 @@ class question_match_qtype extends default_questiontype {
return $status; return $status;
} }
function find_file_links($question, $courseid){
// find links in the question_match_sub table.
$urls = array();
foreach ($question->options->subquestions as $subquestion) {
$urls += question_find_file_links_from_html($subquestion->questiontext, $courseid);
}
//set all the values of the array to the question object
if ($urls){
$urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
}
$urls = array_merge_recursive($urls, parent::find_file_links($question, $courseid));
return $urls;
}
function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
parent::replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination);
// replace links in the question_match_sub table.
if (isset($question->options->subquestions)){
foreach ($question->options->subquestions as $subquestion) {
$subquestionchanged = false;
$subquestion->questiontext = question_replace_file_links_in_html($subquestion->questiontext, $fromcourseid, $tocourseid, $url, $destination, $subquestionchanged);
if ($subquestionchanged){//need to update rec in db
if (!update_record('question_match_sub', addslashes_recursive($subquestion))) {
error('Couldn\'t update \'question_match_sub\' record '.$subquestion->id);
}
}
}
}
}
} }
//// END OF CLASS //// //// END OF CLASS ////

View file

@ -25,15 +25,20 @@ class question_edit_missingtype_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}')); $repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz')); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz')); $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
if (isset($this->question->options)){ if (isset($this->question->options)){
$countanswers = count($this->question->options->answers); $countanswers = count($this->question->options->answers);
} else { } else {
$countanswers = 0; $countanswers = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))? $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD); QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
} else {
$repeatsatstart = $countanswers;
}
$repeatedoptions = array(); $repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_NOTAGS); $mform->setType('answer', PARAM_NOTAGS);
@ -62,7 +67,7 @@ class question_edit_missingtype_form extends question_edit_form {
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;

View file

@ -76,7 +76,7 @@ class question_edit_multianswer_form extends question_edit_form {
function validation($data){ function validation($data){
//TODO would be nice to parse the question text here and output some error //TODO would be nice to parse the question text here and output some error
//messages if there is a problem with the text. //messages if there is a problem with the text.
$errors = array(); $errors = parent::validation($data);
//extra check to make sure there is something in the htmlarea besides a <br /> //extra check to make sure there is something in the htmlarea besides a <br />
$questiontext= trim(strip_tags($data['questiontext'])); $questiontext= trim(strip_tags($data['questiontext']));
if ($questiontext==''){ if ($questiontext==''){

View file

@ -671,6 +671,7 @@ function qtype_multianswer_extract_question($text) {
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) { } else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) {
$wrapped->qtype = 'multichoice'; $wrapped->qtype = 'multichoice';
$wrapped->single = 1; $wrapped->single = 1;
$wrapped->answernumbering = 0;
$wrapped->correctfeedback = ''; $wrapped->correctfeedback = '';
$wrapped->partiallycorrectfeedback = ''; $wrapped->partiallycorrectfeedback = '';
$wrapped->incorrectfeedback = ''; $wrapped->incorrectfeedback = '';

View file

@ -46,15 +46,20 @@ class question_edit_multichoice_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}')); $repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz')); $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
if (isset($this->question->options)){ if (isset($this->question->options)){
$countanswers = count($this->question->options->answers); $countanswers = count($this->question->options->answers);
} else { } else {
$countanswers = 0; $countanswers = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))? $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD); QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
} else {
$repeatsatstart = $countanswers;
}
$repeatedoptions = array(); $repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_RAW); $mform->setType('answer', PARAM_RAW);
@ -62,13 +67,16 @@ class question_edit_multichoice_form extends question_edit_form {
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice')); $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
$mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_multichoice')); $mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_multichoice'),
array('course' => $this->coursefilesid));
$mform->setType('correctfeedback', PARAM_RAW); $mform->setType('correctfeedback', PARAM_RAW);
$mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_multichoice')); $mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_multichoice'),
array('course' => $this->coursefilesid));
$mform->setType('partiallycorrectfeedback', PARAM_RAW); $mform->setType('partiallycorrectfeedback', PARAM_RAW);
$mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_multichoice')); $mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_multichoice'),
array('course' => $this->coursefilesid));
$mform->setType('incorrectfeedback', PARAM_RAW); $mform->setType('incorrectfeedback', PARAM_RAW);
} }
@ -101,7 +109,7 @@ class question_edit_multichoice_form extends question_edit_form {
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;

View file

@ -625,6 +625,36 @@ class question_multichoice_qtype extends default_questiontype {
return 'ERR'; return 'ERR';
} }
} }
function find_file_links($question, $courseid){
$urls = array();
$urls = parent::find_file_links($question, $courseid);
// find links in the question_match_sub table.
foreach ($question->options->subquestions as $subquestion) {
$urls += question_find_file_links_from_html($subquestion->questiontext, $courseid);
}
//set all the values of the array to the question id
if ($urls){
$urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
}
$urls = array_merge_recursive($urls, parent::find_file_links($question, $courseid));
return $urls;
}
function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
parent::replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination);
// replace links in the question_match_sub table.
$optionschanged = false;
$question->options->correctfeedback = question_replace_file_links_in_html($question->options->correctfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
$question->options->partiallycorrectfeedback = question_replace_file_links_in_html($question->options->partiallycorrectfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
$question->options->incorrectfeedback = question_replace_file_links_in_html($question->options->incorrectfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
if ($optionschanged){
if (!update_record('question_multichoice', addslashes_recursive($question->options))) {
error('Couldn\'t update \'question_multichoice\' record '.$question->options->id);
}
}
}
} }
// Register this question type with the question bank. // Register this question type with the question bank.

View file

@ -36,7 +36,8 @@ class question_edit_numerical_form extends question_edit_form {
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz')); $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
$mform->setType('feedback', PARAM_RAW); $mform->setType('feedback', PARAM_RAW);
@ -45,9 +46,12 @@ class question_edit_numerical_form extends question_edit_form {
} else { } else {
$countanswers = 0; $countanswers = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + 1))? $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + 1))?
QUESTION_NUMANS_START : ($countanswers + 1); QUESTION_NUMANS_START : ($countanswers + 1);
} else {
$repeatsatstart = $countanswers;
}
$this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 2, get_string('addmoreanswerblanks', 'qtype_numerical')); $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 2, get_string('addmoreanswerblanks', 'qtype_numerical'));
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
@ -65,7 +69,12 @@ class question_edit_numerical_form extends question_edit_form {
} else { } else {
$countunits = 0; $countunits = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = $countunits + 2; $repeatsatstart = $countunits + 2;
} else {
$repeatsatstart = $countunits;
}
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_numerical')); $this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_numerical'));
$firstunit =& $mform->getElement('multiplier[0]'); $firstunit =& $mform->getElement('multiplier[0]');
@ -99,7 +108,7 @@ class question_edit_numerical_form extends question_edit_form {
parent::set_data($question); parent::set_data($question);
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
// Check the answers. // Check the answers.
$answercount = 0; $answercount = 0;

View file

@ -9,7 +9,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*//** */ */
require_once($CFG->libdir . '/questionlib.php'); require_once($CFG->libdir . '/questionlib.php');
@ -114,7 +114,7 @@ class default_questiontype {
* @param string $submiturl passed on to the constructor call. * @param string $submiturl passed on to the constructor call.
* @return object an instance of the form definition, or null if one could not be found. * @return object an instance of the form definition, or null if one could not be found.
*/ */
function create_editing_form($submiturl, $question) { function create_editing_form($submiturl, $question, $category, $contexts, $formeditable) {
global $CFG; global $CFG;
require_once("{$CFG->dirroot}/question/type/edit_question_form.php"); require_once("{$CFG->dirroot}/question/type/edit_question_form.php");
$definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php'; $definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php';
@ -126,7 +126,7 @@ class default_questiontype {
if (!class_exists($classname)) { if (!class_exists($classname)) {
return null; return null;
} }
return new $classname($submiturl, $question); return new $classname($submiturl, $question, $category, $contexts, $formeditable);
} }
/** /**
@ -154,8 +154,29 @@ class default_questiontype {
* @param string $wizardnow is '' for first page. * @param string $wizardnow is '' for first page.
*/ */
function display_question_editing_page(&$mform, $question, $wizardnow){ function display_question_editing_page(&$mform, $question, $wizardnow){
list($heading, $langmodule) = $this->get_heading(); list($heading, $langmodule) = $this->get_heading(empty($question->id));
print_heading_with_help($heading, $this->name(), $langmodule); print_heading_with_help($heading, $this->name(), $langmodule);
$permissionstrs = array();
if (!empty($question->id)){
if ($question->formoptions->canedit){
$permissionstrs[] = get_string('permissionedit', 'question');
}
if ($question->formoptions->canmove){
$permissionstrs[] = get_string('permissionmove', 'question');
}
if ($question->formoptions->cansaveasnew){
$permissionstrs[] = get_string('permissionsaveasnew', 'question');
}
}
if (!$question->formoptions->movecontext && count($permissionstrs)){
print_heading(get_string('permissionto', 'question'), 'center', 3);
$html = '<ul>';
foreach ($permissionstrs as $permissionstr){
$html .= '<li>'.$permissionstr.'</li>';
}
$html .= '</ul>';
print_box($html, 'boxwidthnarrow boxaligncenter generalbox');
}
$mform->display(); $mform->display();
} }
@ -164,15 +185,20 @@ class default_questiontype {
* *
* @return array a string heading and the langmodule in which it was found. * @return array a string heading and the langmodule in which it was found.
*/ */
function get_heading(){ function get_heading($adding = false){
$name = $this->name(); $name = $this->name();
$langmodule = 'qtype_' . $name; $langmodule = 'qtype_' . $name;
$strheading = get_string('editing' . $name, $langmodule); if (!$adding){
$strtoget = 'editing' . $name;
} else {
$strtoget = 'adding' . $name;
}
$strheading = get_string($strtoget, $langmodule);
if ($strheading[0] == '[') { if ($strheading[0] == '[') {
// Legacy behavior, if the string was not in the proper qtype_name // Legacy behavior, if the string was not in the proper qtype_name
// language file, look it up in the quiz one. // language file, look it up in the quiz one.
$langmodule = 'quiz'; $langmodule = 'quiz';
$strheading = get_string('editing' . $name, $langmodule); $strheading = get_string($strtoget, $langmodule);
} }
return array($strheading, $langmodule); return array($strheading, $langmodule);
} }
@ -204,14 +230,11 @@ class default_questiontype {
* is itself an object, shown next to the form fields. * is itself an object, shown next to the form fields.
*/ */
function save_question($question, $form, $course) { function save_question($question, $form, $course) {
global $USER;
// This default implementation is suitable for most // This default implementation is suitable for most
// question types. // question types.
// First, save the basic question itself // First, save the basic question itself
if (!record_exists('question_categories', 'id', $form->category)) {
print_error('categorydoesnotexist', 'question');
}
$question->category = $form->category;
$question->name = trim($form->name); $question->name = trim($form->name);
$question->questiontext = trim($form->questiontext); $question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat; $question->questiontextformat = $form->questiontextformat;
@ -247,16 +270,28 @@ class default_questiontype {
} }
if (!empty($question->id)) { // Question already exists if (!empty($question->id)) { // Question already exists
if (isset($form->categorymoveto)){
question_require_capability_on($question, 'move');
list($question->categorymoveto, $movetocontextid) = explode(',', $form->categorymoveto);
}
if (isset($question->qtype) && $question->qtype != RANDOM){
$question->category = $question->categorymoveto;
}
// keep existing unique stamp code // keep existing unique stamp code
$question->stamp = get_field('question', 'stamp', 'id', $question->id); $question->stamp = get_field('question', 'stamp', 'id', $question->id);
if (!update_record("question", $question)) { $question->modifiedby = $USER->id;
error("Could not update question!"); $question->timemodified = time();
if (!update_record('question', $question)) {
error('Could not update question!');
} }
} else { // Question is a new one } else { // Question is a new one
// Set the unique code // Set the unique code
list($question->category,$contextid) = explode(',', $form->category);
$question->stamp = make_unique_id_code(); $question->stamp = make_unique_id_code();
if (!$question->id = insert_record("question", $question)) { $question->createdby = $USER->id;
error("Could not insert new question!"); $question->timecreated = time();
if (!$question->id = insert_record('question', $question)) {
error('Could not insert new question!');
} }
} }
@ -703,6 +738,7 @@ class default_questiontype {
// Core question types should not use this mechanism. Their styles // Core question types should not use this mechanism. Their styles
// should be included in the standard theme. // should be included in the standard theme.
// We only do this once // We only do this once
// for this question type, no matter how often this method is called. // for this question type, no matter how often this method is called.
if ($this->already_done) { if ($this->already_done) {
@ -782,7 +818,7 @@ class default_questiontype {
// For editing teachers print a link to an editing popup window // For editing teachers print a link to an editing popup window
$editlink = ''; $editlink = '';
if ($context && has_capability('moodle/question:manage', $context)) { if (question_has_capability_on($question, 'edit')) {
$stredit = get_string('edit'); $stredit = get_string('edit');
$linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />'; $linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />';
$editlink = link_to_popup_window('/question/question.php?inpopup=1&amp;id='.$question->id, 'editquestion', $linktext, 450, 550, $stredit, '', true); $editlink = link_to_popup_window('/question/question.php?inpopup=1&amp;id='.$question->id, 'editquestion', $linktext, 450, 550, $stredit, '', true);
@ -1345,6 +1381,111 @@ class default_questiontype {
return format_text($text, $textformat, $formatoptions, $cmoptions === NULL ? NULL : $cmoptions->course); return format_text($text, $textformat, $formatoptions, $cmoptions === NULL ? NULL : $cmoptions->course);
} }
/*
* Find all course / site files linked from a question.
*
* Need to check for links to files in question_answers.answer and feedback
* and in question table in generalfeedback and questiontext fields. Methods
* on child classes will also check extra question specific fields.
*
* Needs to be overriden for child classes that have extra fields containing
* html.
*
* @param string html the html to search
* @param int courseid search for files for courseid course or set to siteid for
* finding site files.
* @return array of url, relative url is key and array with one item = question id as value
* relative url is relative to course/site files directory root.
*/
function find_file_links($question, $courseid){
$urls = array();
if ($question->image != ''){
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$matches = array();
//support for older questions where we have a complete url in image field
if (preg_match('!^'.question_file_links_base_url($courseid).'(.*)!i', $question->image, $matches)){
if ($cleanedurl = question_url_check($urls[$matches[2]])){
$urls[$cleanedurl] = null;
}
}
} else {
if ($question->image != ''){
if ($cleanedurl = question_url_check($question->image)){
$urls[$cleanedurl] = null;//will be set later
}
}
}
}
$urls += question_find_file_links_from_html($question->questiontext, $courseid);
$urls += question_find_file_links_from_html($question->generalfeedback, $courseid);
if ($this->has_html_answers() && isset($question->options->answers)){
foreach ($question->options->answers as $answerkey => $answer){
$thisurls= question_find_file_links_from_html($answer->answer, $courseid);
if ($thisurls){
$urls += $thisurls;
}
}
}
//set all the values of the array to the question object
if ($urls){
$urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
}
return $urls;
}
/*
* Find all course / site files linked from a question.
*
* Need to check for links to files in question_answers.answer and feedback
* and in question table in generalfeedback and questiontext fields. Methods
* on child classes will also check extra question specific fields.
*
* Needs to be overriden for child classes that have extra fields containing
* html.
*
* @param string html the html to search
* @param int course search for files for courseid course or set to siteid for
* finding site files.
* @return array of files, file name is key and array with one item = question id as value
*/
function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
global $CFG;
$updateqrec = false;
if (!empty($question->image)){
//support for older questions where we have a complete url in image field
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$questionimage = preg_replace('!^'.question_file_links_base_url($fromcourseid).preg_quote($url, '!').'$!i', $destination, $question->image, 1);
} else {
$questionimage = preg_replace('!^'.preg_quote($url, '!').'$!i', $destination, $question->image, 1);
}
if ($questionimage != $question->image){
$question->image = $questionimage;
$updateqrec = true;
}
}
$question->questiontext = question_replace_file_links_in_html($question->questiontext, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
$question->generalfeedback = question_replace_file_links_in_html($question->generalfeedback, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
if ($updateqrec){
if (!update_record('question', addslashes_recursive($question))){
error ('Couldn\'t update question '.$question->name);
}
}
if ($this->has_html_answers() && isset($question->options->answers)){
//answers that do not need updating have been unset
foreach ($question->options->answers as $answer){
$answerchanged = false;
$answer->answer = question_replace_file_links_in_html($answer->answer, $fromcourseid, $tocourseid, $url, $destination, $answerchanged);
if ($answerchanged){
if (!update_record('question_answers', addslashes_recursive($answer))){
error ('Couldn\'t update question ('.$question->name.') answer '.$answer->id);
}
}
}
}
}
/** /**
* @return the best link to pass to print_error. * @return the best link to pass to print_error.
* @param $cmoptions as passed in from outside. * @param $cmoptions as passed in from outside.

View file

@ -32,7 +32,7 @@ class question_edit_random_form extends question_edit_form {
$mform->addElement('header', 'generalheader', get_string("general", 'form')); $mform->addElement('header', 'generalheader', get_string("general", 'form'));
$mform->addElement('questioncategory', 'category', get_string('category', 'quiz'), $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true)); array('contexts' => $this->contexts->having_cap('moodle/question:useall')));
$mform->addElement('text', 'name', get_string('questionname', 'quiz'), $mform->addElement('text', 'name', get_string('questionname', 'quiz'),
array('size' => 50)); array('size' => 50));
@ -57,6 +57,18 @@ class question_edit_random_form extends question_edit_form {
$mform->addElement('hidden', 'versioning'); $mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL); $mform->setType('versioning', PARAM_BOOL);
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0);
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->setDefault('courseid', 0);
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->setDefault('returnurl', 0);
$buttonarray = array(); $buttonarray = array();
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges')); $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));

View file

@ -49,7 +49,7 @@ class question_edit_randomsamatch_form extends question_edit_form {
function validation($data){ function validation($data){
global $QTYPES; global $QTYPES;
$errors = array(); $errors = parent::validation($data);
$saquestions = $QTYPES['randomsamatch']->get_sa_candidates($data['category']); $saquestions = $QTYPES['randomsamatch']->get_sa_candidates($data['category']);
$numberavailable = count($saquestions); $numberavailable = count($saquestions);
if ($saquestions === false){ if ($saquestions === false){

View file

@ -31,15 +31,20 @@ class question_edit_shortanswer_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerno', 'qtype_shortanswer', '{no}')); $repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerno', 'qtype_shortanswer', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 54)); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 54));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz')); $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
if (isset($this->question->options)){ if (isset($this->question->options)){
$countanswers = count($this->question->options->answers); $countanswers = count($this->question->options->answers);
} else { } else {
$countanswers = 0; $countanswers = 0;
} }
if ($this->question->formoptions->repeatelements){
$repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))? $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD); QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
} else {
$repeatsatstart = $countanswers;
}
$repeatedoptions = array(); $repeatedoptions = array();
$mform->setType('answer', PARAM_RAW); $mform->setType('answer', PARAM_RAW);
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
@ -65,7 +70,7 @@ class question_edit_shortanswer_form extends question_edit_form {
parent::set_data($question); parent::set_data($question);
} }
function validation($data){ function validation($data){
$errors = array(); $errors = parent::validation($data);
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;

View file

@ -23,10 +23,12 @@ class question_edit_truefalse_form extends question_edit_form {
$mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'), $mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'),
array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse'))); array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse')));
$mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse')); $mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'),
array('course' => $this->coursefilesid));;
$mform->setType('feedbacktrue', PARAM_RAW); $mform->setType('feedbacktrue', PARAM_RAW);
$mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse')); $mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'),
array('course' => $this->coursefilesid));
$mform->setType('feedbackfalse', PARAM_RAW); $mform->setType('feedbackfalse', PARAM_RAW);
// Fix penalty factor at 1. // Fix penalty factor at 1.

View file

@ -8,7 +8,7 @@
* @author T.J.Hunt@open.ac.uk * @author T.J.Hunt@open.ac.uk
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package package_name * @package package_name
*//** */ */
/** /**
* This test is becuase the RQP question type was included in core * This test is becuase the RQP question type was included in core
@ -19,7 +19,7 @@
* question type was never installed, or if the person has chosen to * question type was never installed, or if the person has chosen to
* manually reinstall the rqp question type from contrib. * manually reinstall the rqp question type from contrib.
* *
* @param $version the version to test. * @param $result the result object that can be modified.
* @return null if the test is irrelevant, or true or false depending on whether the test passes. * @return null if the test is irrelevant, or true or false depending on whether the test passes.
*/ */
function question_check_no_rqp_questions($result) { function question_check_no_rqp_questions($result) {
@ -70,4 +70,254 @@ function question_remove_rqp_qtype_config_string() {
return $result; return $result;
} }
/**
* @param $result the result object that can be modified.
* @return null if the test is irrelevant, or true or false depending on whether the test passes.
*/
function question_random_check($result){
global $CFG;
if ($CFG->version >= 2007081000){
return null;//no test after upgrade seperates question cats into contexts.
}
if (!$toupdate = question_cwqpfs_to_update()){
$result->setStatus(true);//pass test
} else {
//set the feedback string here and not in xml file since we need something
//more complex than just a string picked from admin.php lang file
$a = new object();
$a->reporturl = "{$CFG->wwwroot}/{$CFG->admin}/report/question/";
$lang = str_replace('_utf8', '', current_language());
$a->docsurl = "{$CFG->docroot}/$lang/admin/report/question/index";
$result->feedback_str = get_string('questioncwqpfscheck', 'admin', $a);
$result->setStatus(false);//fail test
}
return $result;
}
/*
* Delete all 'random' questions that are not been used in a quiz.
*/
function question_delete_unused_random(){
global $CFG;
$tofix = array();
$result = true;
//delete all 'random' questions that are not been used in a quiz.
if ($qqis = get_records_sql("SELECT q.* FROM {$CFG->prefix}question as q LEFT JOIN ".
"({$CFG->prefix}quiz_question_instances as qqi) ".
"ON (q.id = qqi.question) WHERE q.qtype='random' AND qqi.question IS NULL")){
$qqilist = join(array_keys($qqis), ',');
$result = $result && delete_records_select('question', "id IN ($qqilist)");
}
return $result;
}
function question_cwqpfs_to_update($categories = null){
global $CFG;
$tofix = array();
$result = true;
//any cats with questions picking from subcats?
if (!$cwqpfs = get_records_sql_menu("SELECT DISTINCT qc.id, 1 ".
"FROM {$CFG->prefix}question as q, {$CFG->prefix}question_categories as qc ".
"WHERE q.qtype='random' AND qc.id = q.category AND q.questiontext = 1")){
return array();
} else {
if ($categories === null){
$categories = get_records('question_categories');
}
$categorychildparents = array();
foreach ($categories as $id => $category){
$categorychildparents[$category->course][$id] = $category->parent;
}
foreach ($categories as $id => $category){
if (FALSE !== array_key_exists($category->parent, $categorychildparents[$category->course])){
//this is not a top level cat
continue;//go to next category
} else{
$tofix += question_cwqpfs_check_children($id, $categories, $categorychildparents[$category->course], $cwqpfs);
}
}
}
return $tofix;
}
function question_cwqpfs_check_children($checkid, $categories, $categorychildparents, $cwqpfs){
$tofix = array();
if (array_key_exists($checkid, $cwqpfs)){//cwqpfs in this cat
$getchildren = array();
$getchildren[] = $checkid;
//search down tree and find all children
while ($nextid = array_shift($getchildren)){//repeat until $getchildren
//empty;
$childids = array_keys($categorychildparents, $nextid);
foreach ($childids as $childid){
if ($categories[$childid]->publish != $categories[$checkid]->publish){
$tofix[$childid] = $categories[$checkid]->publish;
}
}
$getchildren = array_merge($getchildren, $childids);
}
} else { // check children for cwqpfs
$childrentocheck = array_keys($categorychildparents, $checkid);
foreach ($childrentocheck as $childtocheck){
$tofix += question_cwqpfs_check_children($childtocheck, $categories, $categorychildparents, $cwqpfs);
}
}
return $tofix;
}
function question_category_next_parent_in($contextid, $question_categories, $id){
$nextparent = $question_categories[$id]->parent;
if ($nextparent == 0){
return 0;
} elseif (!array_key_exists($nextparent, $question_categories)){
//finished searching up the category hierarchy. For some reason
//the top level items is not 0. We'll return 0 though.
return 0;
} elseif ($contextid == $question_categories[$nextparent]->contextid){
return $nextparent;
} else {
//parent is not in the same context look further up.
return question_category_next_parent_in($contextid, $question_categories, $nextparent);
}
}
/**
* Check that either category parent is 0 or a category shared in the same context.
* Fix any categories to point to grand or grand grand parent etc in the same context or 0.
*/
function question_category_checking($question_categories){
//make an array that is easier to search
$newparents = array();
foreach ($question_categories as $id => $category){
$newparents[$id] = question_category_next_parent_in($category->contextid, $question_categories, $id);
}
foreach (array_Keys($question_categories) as $id){
$question_categories[$id]->parent = $newparents[$id];
}
return $question_categories;
}
function question_upgrade_context_etc(){
global $CFG;
$result = true;
$result = $result && question_delete_unused_random();
$question_categories = get_records('question_categories');
$tofix = question_cwqpfs_to_update($question_categories);
foreach ($tofix as $catid => $publish){
$question_categories[$catid]->publish = $publish;
}
foreach ($question_categories as $id => $question_category){
$course = $question_categories[$id]->course;
unset($question_categories[$id]->course);
if ($question_categories[$id]->publish){
$context = get_context_instance(CONTEXT_SYSTEM);
} else {
$context = get_context_instance(CONTEXT_COURSE, $course);
}
$question_categories[$id]->contextid = $context->id;
unset($question_categories[$id]->publish);
}
$question_categories = question_category_checking($question_categories);
/// Define index course (not unique) to be dropped form question_categories
$table = new XMLDBTable('question_categories');
$index = new XMLDBIndex('course');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course'));
/// Launch drop index course
$result = $result && drop_index($table, $index);
/// Define field course to be dropped from question_categories
$field = new XMLDBField('course');
/// Launch drop field course
$result = $result && drop_field($table, $field);
/// Define field context to be added to question_categories
$field = new XMLDBField('contextid');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'name');
$field->comment = 'context that this category is shared in';
/// Launch add field context
$result = $result && add_field($table, $field);
/// Define index context (not unique) to be added to question_categories
$index = new XMLDBIndex('contextid');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('contextid'));
$index->comment = 'links to context table';
/// Launch add index context
$result = $result && add_index($table, $index);
$field = new XMLDBField('publish');
/// Launch drop field publish
$result = $result && drop_field($table, $field);
/// update table contents with previously calculated new contents.
foreach ($question_categories as $question_category){
if (!$result = update_record('question_categories', $question_category)){
notify('Couldn\'t update question_categories "'. $question_category->name .'"!');
}
}
/// Define field timecreated to be added to question
$table = new XMLDBTable('question');
$field = new XMLDBField('timecreated');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'hidden');
/// Launch add field timecreated
$result = $result && add_field($table, $field);
/// Define field timemodified to be added to question
$table = new XMLDBTable('question');
$field = new XMLDBField('timemodified');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'timecreated');
/// Launch add field timemodified
$result = $result && add_field($table, $field);
/// Define field createdby to be added to question
$table = new XMLDBTable('question');
$field = new XMLDBField('createdby');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'timemodified');
/// Launch add field createdby
$result = $result && add_field($table, $field);
/// Define field modifiedby to be added to question
$table = new XMLDBTable('question');
$field = new XMLDBField('modifiedby');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'createdby');
/// Launch add field modifiedby
$result = $result && add_field($table, $field);
/// Define key createdby (foreign) to be added to question
$table = new XMLDBTable('question');
$key = new XMLDBKey('createdby');
$key->setAttributes(XMLDB_KEY_FOREIGN, array('createdby'), 'user', array('id'));
/// Launch add key createdby
$result = $result && add_key($table, $key);
/// Define key modifiedby (foreign) to be added to question
$table = new XMLDBTable('question');
$key = new XMLDBKey('modifiedby');
$key->setAttributes(XMLDB_KEY_FOREIGN, array('modifiedby'), 'user', array('id'));
/// Launch add key modifiedby
$result = $result && add_key($table, $key);
return $result;
}
?> ?>

View file

@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine // This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php) // whether upgrades should be performed (see lib/db/*.php)
$version = 2007080903; // YYYYMMDD = date $version = 2007081000; // YYYYMMDD = date
// XY = increments within a single day // XY = increments within a single day
$release = '1.9 dev'; // Human-friendly version name $release = '1.9 dev'; // Human-friendly version name