--- /dev/null
+<?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().
+ "&edit=$id&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}&id={$question->id}&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();
+?>
)
),
- 'moodle/question:import' => array(
-
- 'riskbitmask' => RISK_XSS,
+ 'moodle/question:managecategory' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'editingteacher' => CAP_ALLOW,
'admin' => CAP_ALLOW
)
+ ),
+
+ //new in moodle 1.9
+ 'moodle/question:add' => array(
+ 'riskbitmask' => RISK_SPAM,
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_COURSE,
+ 'legacy' => array(
+ 'editingteacher' => CAP_ALLOW,
+ 'admin' => CAP_ALLOW
+ ),
+ 'clonepermissionsfrom' => 'moodle/question:manage'
),
-
- 'moodle/question:export' => array(
-
+ '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:managecategory' => array(
-
+ '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:manage' => array(
-
- 'riskbitmask' => RISK_XSS,
-
+ '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.
'moodle/question:config' => array(
if ($module = get_record('modules', 'name', 'exercise')) {
if ($module->visible) {
// Hide/disable the module entry
- set_field('modules', 'visible', '0', 'id', $module->id);
+ set_field('modules', 'visible', '0', 'id', $module->id);
// Save existing visible state for all activities
set_field('course_modules', 'visibleold', '1', 'visible' ,'1', 'module', $module->id);
set_field('course_modules', 'visibleold', '0', 'visible' ,'0', 'module', $module->id);
// Hide all activities
set_field('course_modules', 'visible', '0', 'module', $module->id);
-
+
require_once($CFG->dirroot.'/course/lib.php');
rebuild_course_cache(); // Rebuld cache for all modules because they might have changed
}
set_field('modules', 'visible', 0, 'name', 'lams'); // Disable it by default
}
}
-
+
if ($result && $oldversion < 2006102600) {
/// Define fields to be added to user_info_field
$result = $result && add_field($table, $field4);
$result = $result && add_field($table, $field5);
}
-
+
if ($result && $oldversion < 2006112000) {
/// Define field attachment to be added to post
/// Launch add field attachment
$result = $result && add_field($table, $field);
}
-
+
if ($result && $oldversion < 2006112200) {
/// Define field imagealt to be added to user
/// Launch add field imagealt
$result = $result && add_field($table, $field);
-
+
$table = new XMLDBTable('user');
$field = new XMLDBField('screenreader');
$field->setAttributes(XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null, null, '0', 'imagealt');
if ($oldversion < 2006120400) { /// Remove secureforms config setting
execute_sql("DELETE FROM {$CFG->prefix}config where name='secureforms'", true);
}
-
+
if (!empty($CFG->rolesactive) && $oldversion < 2006120700) { // add moodle/user:viewdetails to all roles!
// note: use of assign_capability() is discouraged in upgrade script!
if ($roles = get_records('role')) {
XMLDB_NOTNULL, null, null, null, 0);
$f = $table->addFieldInfo('last_log_id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
XMLDB_NOTNULL, null, null, null, 0);
- // PK and indexes
+ // PK and indexes
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
// Create the table
$result = $result && create_table($table);
$result = $result && create_table($table);
//
- // Prime MNET configuration entries -- will be needed later by auth/mnet
+ // Prime MNET configuration entries -- will be needed later by auth/mnet
//
include_once $CFG->dirroot . '/mnet/lib.php';
$env = new mnet_environment();
$env->init();
unset($env);
- // add mnethostid to user-
+ // add mnethostid to user-
$table = new XMLDBTable('user');
$field = new XMLDBField('mnethostid');
$field->setType(XMLDB_TYPE_INTEGER);
// The default mnethostid is zero... we need to update this for all
// users of the local IdP service.
- set_field('user',
- 'mnethostid', $CFG->mnet_localhost_id,
+ set_field('user',
+ 'mnethostid', $CFG->mnet_localhost_id,
'mnethostid', '0');
notify(get_string('duplicate_usernames', 'mnet', 'http://docs.moodle.org/en/DuplicateUsernames'));
}
- unset($table, $field, $index);
+ unset($table, $field, $index);
/**
** auth/mnet tables
$f = $table->addFieldInfo('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
XMLDB_NOTNULL, NULL, null, null, 0);
$f = $table->addFieldInfo('hostid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
- XMLDB_NOTNULL, NULL, null, null, 0);
+ XMLDB_NOTNULL, NULL, null, null, 0);
$f = $table->addFieldInfo('courseid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
XMLDB_NOTNULL, NULL, null, null, 0);
$f = $table->addFieldInfo('rolename', XMLDB_TYPE_CHAR, '255', null,
/// Launch create table for context_rel
$result = $result && create_table($table);
-
+
/// code here to fill the context_rel table
/// use get record set to iterate slower
build_context_rel();
}
if ($result && $oldversion < 2007011501) {
- if (!empty($CFG->enablerecordcache) && empty($CFG->rcache) &&
+ if (!empty($CFG->enablerecordcache) && empty($CFG->rcache) &&
// Note: won't force-load these settings into CFG
// we don't need or want cache during the upgrade itself
empty($CFG->cachetype) && empty($CFG->intcachemax)) {
/// Launch drop index text
$result = $result && drop_index($table, $index);
-
+
$field = new XMLDBField('text');
$field->setAttributes(XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null, null, 'userid');
/// Launch change of type for field text
$result = $result && change_field_type($table, $field);
-
+
$index = new XMLDBIndex('text');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('text'));
/// Launch add index text
- $result = $result && add_index($table, $index);
+ $result = $result && add_index($table, $index);
}
-
+
if ($result && $oldversion < 2007041100) {
/// Define field idnumber to be added to course_modules
$table = new XMLDBTable('course_modules');
$field = new XMLDBField('idnumber');
$field->setAttributes(XMLDB_TYPE_CHAR, '100', null, null, null, null, null, null, 'section');
-
+
/// Launch add field idnumber
$result = $result && add_field($table, $field);
-
+
/// Define index idnumber (unique) to be added to course_modules
$table = new XMLDBTable('course_modules');
$index = new XMLDBIndex('idnumber');
We could do all this with one tricky SQL statement but it's a one-off so no
harm in using PHP loops */
if ($result && $oldversion < 2007041600) {
-
+
/// Get the menu fields
if ($fields = get_records('user_info_field', 'datatype', 'menu')) {
foreach ($fields as $field) {
$options = explode("\n", $this->field->param1);
foreach ($data as $d) {
$key = array_search($d->data, $options);
-
+
/// If the data is an integer and is not one of the options,
/// set the respective option value
if (is_int($d->data) and (($key === NULL) or ($key === false)) and isset($options[$d->data])) {
}
}
}
-
+
}
-
+
/// adding new gradebook tables
if ($result && $oldversion < 2007041800) {
/// Launch create table for events_handlers
$result = $result && create_table($table);
-
+
/// Define table events_queue to be created
$table = new XMLDBTable('events_queue');
/// Launch create table for events_queue
$result = $result && create_table($table);
-
+
/// Define table events_queue_handlers to be created
$table = new XMLDBTable('events_queue_handlers');
/// Launch add field schedule
$result = $result && add_field($table, $field);
-
+
/// Define field status to be added to events_handlers
$table = new XMLDBTable('events_handlers');
$field = new XMLDBField('status');
/// Launch add field usermodified
$result = $result && add_field($table, $field);
-
+
/// Define key usermodified (foreign) to be added to post
$table = new XMLDBTable('post');
$key = new XMLDBKey('usermodified');
$application->xmlrpc_server_url = '/api/xmlrpc/server.php';
$application->sso_land_url = '/auth/xmlrpc/land.php';
$result = $result && insert_record('mnet_application', $application, false);
-
+
// New mnet_host->applicationid field
$table = new XMLDBTable('mnet_host');
$field = new XMLDBField('applicationid');
$result = $result && question_remove_rqp_qtype_config_string();
}
-
if ($result && $oldversion < 2007072200) {
/// Remove obsoleted unit tests tables - they will be recreated automatically
$tables = array('grade_categories',
if ($result && $oldversion < 2007073100) {
- /// Define table grade_outcomes_courses to be created
- $table = new XMLDBTable('grade_outcomes_courses');
-
- /// Adding fields to table grade_outcomes_courses
- $table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
- $table->addFieldInfo('courseid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
- $table->addFieldInfo('outcomeid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
-
- /// Adding keys to table grade_outcomes_courses
- $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
- $table->addKeyInfo('courseid', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id'));
- $table->addKeyInfo('outcomeid', XMLDB_KEY_FOREIGN, array('outcomeid'), 'grade_outcomes', array('id'));
-
- /// Launch create table for grade_outcomes_courses
- $result = $result && create_table($table);
+ /// Define table grade_outcomes_courses to be created
+ $table = new XMLDBTable('grade_outcomes_courses');
+
+ /// Adding fields to table grade_outcomes_courses
+ $table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
+ $table->addFieldInfo('courseid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+ $table->addFieldInfo('outcomeid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+
+ /// Adding keys to table grade_outcomes_courses
+ $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table->addKeyInfo('courseid', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id'));
+ $table->addKeyInfo('outcomeid', XMLDB_KEY_FOREIGN, array('outcomeid'), 'grade_outcomes', array('id'));
+
+ /// Launch create table for grade_outcomes_courses
+ $result = $result && create_table($table);
}
$result = $result && add_field($table, $field);
}
}
-
+
// adding unique contraint on (courseid,shortname) of an outcome
if ($result && $oldversion < 2007080100) {
set_config('supportemail', s($firstadmin->email));
}
}
-
+
/// MDL-10679, context_rel clean up
if ($result && $oldversion < 2007080200) {
delete_records('context_rel');
}
}
*/
+ //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;
}
+
+
?>
/**
* A moodle form field type for question categories.
*
- * @copyright © 2006 The Open University
- * @author T.J.Hunt@open.ac.uk
+ * @copyright Jamie Pratt
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodleforms
- *//** */
+ */
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.
* @access public
*/
-class MoodleQuickForm_questioncategory extends MoodleQuickForm_select {
+class MoodleQuickForm_questioncategory extends MoodleQuickForm_selectgroups {
+ var $_options = array('top'=>false, 'currentcat'=>0, 'nochildrenof' => -1);
/**
* Constructor
* @access public
* @return void
*/
- function MoodleQuickForm_questioncategory($elementName = null,
- $elementLabel = null, $attributes = null, $options = null) {
- HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes, null);
-
- global $COURSE;
+ function MoodleQuickForm_questioncategory($elementName = null, $elementLabel = null, $options = null, $attributes = null) {
+ MoodleQuickForm_selectgroups::MoodleQuickForm_selectgroups($elementName, $elementLabel, array(), $attributes);
$this->_type = 'questioncategory';
- if (!empty($options['courseid'])) {
- $this->_courseid = $options['courseid'];
- } else {
- $this->_courseid = $COURSE->id;
- }
- 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;
+ if (is_array($options)) {
+ $this->_options = $options + $this->_options;
+ $this->loadArrayOptGroups(
+ question_category_options($this->_options['contexts'], $this->_options['top'], $this->_options['currentcat'],
+ false, $this->_options['nochildrenof']));
}
}
- /**
- * 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);
- }
}
?>
\ No newline at end of file
// 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
// that are included with the following library
- require_once("$CFG->libdir/questionlib.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
- function insert_category_ids($course, $backup_unique_code, $instances = null) {
+ /*
+ * Insert necessary category ids to backup_ids table. Called during backup_check.html
+ */
+ function insert_category_and_question_ids($course, $backup_unique_code, $instances = null) {
global $CFG;
- include_once("$CFG->dirroot/mod/quiz/lib.php");
// Create missing categories and reasign orphaned questions.
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
(backup_code, table_name, old_id, info)
SELECT '$backup_unique_code', 'question_categories', qc.id, ''
FROM {$CFG->prefix}question_categories qc
- WHERE qc.course = $course", false);
-
- // Then published categories from other courses used by the quizzes we are backing up.
+ WHERE qc.contextid = {$coursecontext->id}", false);
+
+
+ // 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)) {
$questionselectsqlfrom = '';
$questionselectsqlwhere = 'AND qqi.quiz IN ('.implode(',',array_keys($instances)).')';
$categoriesinothercourses = get_records_sql("
SELECT id, parent, 0 AS childrendone
FROM {$CFG->prefix}question_categories
- WHERE course <> $course
+ WHERE contextid IN (".join($parentcontexts, ', ').")
AND id IN (
SELECT DISTINCT question.category
FROM {$CFG->prefix}question question,
WHERE qqi.question = question.id
$questionselectsqlwhere
)", false);
- if (!$categoriesinothercourses) {
- $categoriesinothercourses = array();
- }
-
- // Add the parent categories, of these categories up to the top of the category tree.
- foreach ($categoriesinothercourses as $category) {
- while ($category->parent != 0) {
- if (array_key_exists($category->parent, $categoriesinothercourses)) {
- // Parent category already on the list.
- break;
- }
- $currentid = $category->id;
- $category = get_record('question_categories', 'id', $category->parent, '', '', '', '', 'id, parent, 0 AS childrendone');
- if ($category) {
- $categoriesinothercourses[$category->id] = $category;
- } else {
- // Parent not found: this indicates an error, but just fix it.
- set_field('question_categories', 'parent', 0, 'id', $currentid);
- break;
+ if (!$categories) {
+ $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.
+ // not backing up the questions in these categories.
+ foreach ($categories as $category) {
+ while ($category->parent != 0) {
+ if (array_key_exists($category->parent, $categories)) {
+ // Parent category already on the list.
+ break;
+ }
+ $currentid = $category->id;
+ $category = get_record('question_categories', 'id', $category->parent, '', '', '', '', 'id, parent, 0 AS childrendone');
+ if ($category) {
+ $categories[$category->id] = $category;
+ } else {
+ // Parent not found: this indicates an error, but just fix it.
+ set_field('question_categories', 'parent', 0, 'id', $currentid);
+ break;
+ }
}
}
- }
- // Now we look for random questions used in our quizzes
- // that select from subcategories in other courses. That implies
- // those subcategories also need to be backed up. (The categories themselves
- // and their parents will already have been included.)
- $categorieswithrandom = get_records_sql("
- SELECT DISTINCT question.category AS id
- FROM {$CFG->prefix}quiz_question_instances qqi,
- $questionselectsqlfrom
- {$CFG->prefix}question question
- WHERE question.id = qqi.question
- AND question.qtype = '" . RANDOM . "'
- AND question.questiontext = '1'
- $questionselectsqlwhere
- ");
- if ($categorieswithrandom) {
- foreach ($categorieswithrandom as $category) {
- if (isset($categoriesinothercourses[$category->id])){
- $status = quiz_backup_add_sub_categories($categoriesinothercourses, $category->id);
+ // Now we look for categories from other courses containing random questions
+ // in our quizzes that select from the category and its subcategories. That implies
+ // those subcategories also need to be backed up. (The categories themselves
+ // and their parents will already have been included.)
+ $categorieswithrandom = get_records_sql("
+ SELECT question.category AS id, SUM(question.questiontext) as questiontext
+ FROM {$CFG->prefix}quiz_question_instances qqi,
+ $from
+ {$CFG->prefix}question question
+ WHERE question.id = qqi.question
+ AND question.qtype = '" . RANDOM . "'
+ $where
+ GROUP BY question.category
+ ");
+ $randomselectedquestions = array();
+ if ($categorieswithrandom) {
+ foreach ($categorieswithrandom as $category) {
+ if ($category->questiontext){
+ $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.
- foreach ($categoriesinothercourses as $category) {
- $status = $status && backup_putid($backup_unique_code, 'question_categories', $category->id, 0);
+ // Finally, add all these extra categories to the backup_ids table.
+ foreach ($categories as $category) {
+ $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;
}
/**
* 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;
if ($categories[$categoryid]->childrendone) {
return $status;
if (!array_key_exists($subcategory->id, $categories)) {
$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;
if (!$exist) {
//Build a new category
$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->info = get_string('recreatedcategory','',$key);
- $db_cat->publish = 1;
$db_cat->stamp = make_unique_id_code();
//Insert the new category
$catid = insert_record('question_categories',$db_cat);
////Return an array of info (name,value)
/// $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) {
-
//Deletes data from mdl_backup_ids (categories section)
- delete_category_ids ($backup_unique_code);
- //Create date into mdl_backup_ids (categories section)
- insert_category_ids ($course,$backup_unique_code,$instances);
+ delete_ids ($backup_unique_code, 'question_categories');
+ delete_ids ($backup_unique_code, 'question');
+ //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)) {
$info = array();
foreach ($instances as $id => $instance) {
<?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"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="question_categories" COMMENT="Categories are for grouping questions" NEXT="question">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="course"/>
- <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="course" NEXT="info"/>
- <FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="name" NEXT="publish"/>
- <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="publish" NEXT="parent"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="name"/>
+ <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="id" NEXT="contextid"/>
+ <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="contextid" NEXT="stamp"/>
+ <FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="info" 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="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" ENUM="false" PREVIOUS="parent"/>
</FIELDS>
<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>
<INDEXES>
- <INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
+ <INDEX NAME="contextid" UNIQUE="false" FIELDS="contextid" COMMENT="links to context table"/>
</INDEXES>
</TABLE>
<TABLE NAME="question" COMMENT="The questions themselves" PREVIOUS="question_categories" NEXT="question_answers">
<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="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>
<KEYS>
<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="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>
</TABLE>
<TABLE NAME="question_answers" COMMENT="Answers, with a fractional grade (0-1) and feedback" PREVIOUS="question" NEXT="question_dataset_definitions">
<?php //$Id$
-// This file keeps track of upgrades to
+// This file keeps track of upgrades to
// the quiz module
//
// Sometimes, changes between versions involve
function xmldb_quiz_upgrade($oldversion=0) {
- global $CFG, $THEME, $db;
+ global $CFG, $THEME;
$result = true;
-/// And upgrade begins here. For each one, you'll need one
-/// block of code similar to the next one. Please, delete
+/// And upgrade begins here. For each one, you'll need one
+/// block of code similar to the next one. Please, delete
/// this comment lines once this file start handling proper
/// upgrade code.
// Separate control for when overall feedback is displayed, independant of the question feedback settings.
if ($result && $oldversion < 2007072600) {
-
+
// Adjust the quiz review options so that overall feedback is displayed whenever feedback is.
$result = $result && execute_sql('UPDATE ' . $CFG->prefix . 'quiz SET review = ' .
sql_bitor(sql_bitand('review', sql_bitnot(QUIZ_REVIEW_OVERALLFEEDBACK)),
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN) << 14) |
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED) << 12));
}
-
+
return $result;
}
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.
*/
- function module_specific_actions($pageurl, $questionid, $cmid){
+ function module_specific_actions($pageurl, $questionid, $cmid, $canuse){
global $CFG;
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
+ if ($canuse){
$straddtoquiz = get_string("addtoquiz", "quiz");
$out = "<a title=\"$straddtoquiz\" href=\"edit.php?".$pageurl->get_query_string()."&addquestion=$questionid&sesskey=".sesskey()."\"><img
src=\"$CFG->pixpath/t/moveleft.gif\" alt=\"$straddtoquiz\" /></a> ";
}
}
/**
- * 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_buttons($cmid){
global $THEME;
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
- $straddtoquiz = get_string("addtoquiz", "quiz");
- $out = "<input type=\"submit\" name=\"add\" value=\"{$THEME->larrow} $straddtoquiz\" />\n";
- $out .= '</td><td>';
- return $out;
- } else {
- return '';
- }
+ $straddtoquiz = get_string("addtoquiz", "quiz");
+ $out = "<input type=\"submit\" name=\"add\" value=\"{$THEME->larrow} $straddtoquiz\" />\n";
+ echo '<br />';
+ return $out;
}
-
-
+
+
/**
- * Callback function called from question_list() function (which is called from showbank()
- * Displays button in form with checkboxes for each question.
+ * Callback function called from question_list() function (which is called from showbank())
*/
- function module_specific_controls($totalnumber, $recurse, $categoryid, $cmid){
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
+ function module_specific_controls($totalnumber, $recurse, $category, $cmid){
+ $catcontext = get_context_instance_by_id($category->contextid);
+ if (has_capability('moodle/question:useall', $catcontext)){
for ($i = 1;$i <= min(10, $totalnumber); $i++) {
$randomcount[$i] = $i;
}
$out = '<br />';
$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=\"categoryid\" value=\"$categoryid\" />";
+ $out .= "<input type=\"hidden\" name=\"categoryid\" value=\"$category->id\" />";
$out .= ' <input type="submit" name="addrandom" value="'. get_string('add') .'" />';
$out .= helpbutton('random', get_string('random', 'quiz'), 'quiz', true, false, '', true);
- return $out;
} else {
- return '';
+ $out = '';
}
- }
-
- list($thispageurl, $courseid, $cmid, $cm, $quiz, $pagevars) = question_edit_setup(true);
+ return $out;
+ }
+
+ 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
//otherwise they would go in question_edit_setup
if ($quiz_reordertool != 0) {
$thispageurl->param('reordertool', $quiz_reordertool);
}
-
+
$strquizzes = get_string('modulenameplural', 'quiz');
$strquiz = get_string('modulename', 'quiz');
$streditingquestions = get_string('editquestions', "quiz");
$streditingquiz = get_string('editinga', 'moodle', $strquiz);
-
+
if (! $course = get_record("course", "id", $quiz->course)) {
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.
add_to_log($cm->course, 'quiz', 'editquestions',
"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)
&& empty($quiz->grades)){ // Construct an array to hold all the grades.
if (! $category = get_record('question_categories', 'id', $categoryid)) {
error('Category ID is incorrect');
}
+ $catcontext = get_context_instance_by_id($category->contextid);
+ require_capability('moodle/question:useall', $catcontext);
$category->name = addslashes($category->name);
// find existing random questions in this category
$random = RANDOM;
if ($randomcreate > 0) {
$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
// on whether to include questions in subcategories
$form->questiontextformat = 0;
error('Could not save layout');
}
}
-
if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz
quiz_delete_quiz_question($_REQUEST['delete'], $quiz);
}
$questions[$value] = $oldquestions[$key];
}
}
-
+
// If ordering info was given, reorder the questions
if ($questions) {
ksort($questions);
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)) {
- $category = get_default_question_category($course->id);
- $quiz->category = $category->id;
- }
+/// all commands have been dealt with, now print the page
// Print basic page layout.
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
- $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'))
: "";
$navlinks = array();
- $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
- $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
+ $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
+ $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
$navlinks[] = array('name' => $streditingquiz, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($streditingquiz, '', $navigation, "", "",
true, $strupdatemodule);
$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->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 "<a href=\"report.php?mode=overview&id=$cm->id\">".get_string('numattempts', 'quiz', $a)."</a><br />".get_string("attemptsexist","quiz");
echo "</div><br />\n";
}
// 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'))
: "";
$navlinks = array();
- $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
- $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
+ $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
+ $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
$navlinks[] = array('name' => $streditingquiz, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($streditingquiz, '', $navigation, "", "", true, $strupdatemodule);
$currenttab = 'edit';
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']);
echo '</td></tr>';
$strgrade = get_string("grade");
$strremove = get_string('remove', 'quiz');
$stredit = get_string("edit");
+ $strview = get_string("view");
$straction = get_string("action");
$strmoveup = get_string("moveup");
$strmovedown = get_string("movedown");
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,
{$CFG->prefix}question_categories c
WHERE q.id in ($quiz->questions)
$count = 0;
$qno = 1;
$sumgrade = 0;
- $order = explode(",", $quiz->questions);
+ $order = explode(',', $quiz->questions);
$lastindex = count($order)-1;
// If the list does not end with a pagebreak then add it on.
if ($order[$lastindex] != 0) {
echo '<td colspan="2"> </td>';
}
$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>";
continue;
}
$question = $questions[$qnum];
- $canedit = has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $question->course));
echo "<td>";
if ($count != 0) {
}
echo '</td><td align="center">';
- if ($question->qtype != 'random') {
- quiz_question_preview_button($quiz, $question);
+ if (($question->qtype != 'random')){
+ echo quiz_question_preview_button($quiz, $question);
}
- if ($canedit) {
- $returnurl = $pageurl->out();
- $questionparams = array('returnurl' => $returnurl, 'cmid'=>$quiz->cmid, 'id' => $qnum);
- $questionurl = new moodle_url("$CFG->wwwroot/question/question.php", $questionparams);
+ $returnurl = $pageurl->out();
+ $questionparams = array('returnurl' => $returnurl, 'cmid'=>$quiz->cmid, 'id' => $question->id);
+ $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()."\">
<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> ";
}
- 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))."\">
<img src=\"$CFG->pixpath/t/removeright.gif\" class=\"iconsmall\" alt=\"$strremove\" /></a>";
}
// Print the header
$strquizzes = get_string("modulenameplural", "quiz");
$streditquestions = '';
- if (has_capability('moodle/question:manage', $coursecontext)) {
+ $editqcontexts = new question_edit_contexts($coursecontext);
+ if ($editqcontexts->have_one_edit_tab_cap('questions')) {
$streditquestions =
"<form target=\"_parent\" method=\"get\" action=\"$CFG->wwwroot/question/edit.php\">
<div>
$navlinks = array();
$navlinks[] = array('name' => $strquizzes, 'link' => '', 'type' => 'activity');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($strquizzes, '', $navigation,
'', '', true, $streditquestions, navmenu($course));
return;
}
}
-
+
if ($attempt->quiz != $quiz->id) {
debugging("Trying to delete attempt $attempt->id which belongs to quiz $attempt->quiz " .
"but was passed quiz $quiz->id.");
return;
}
-
+
delete_records('quiz_attempts', 'id', $attempt->id);
delete_attempt($attempt->uniqueid);
*/
function quiz_question_preview_button($quiz, $question) {
global $CFG;
+ if (!question_has_capability_on($question, 'use', $question->category)){
+ return '';
+ }
$strpreview = get_string('previewquestion', 'quiz');
return link_to_popup_window('/question/preview.php?id=' . $question->id . '&quizid=' . $quiz->id, 'questionpreview',
"<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />",
if (!isset($cm)) {
$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);
+if (!isset($contexts)){
+ $contexts = new question_edit_contexts($context);
+}
$tabs = array();
$row = array();
$inactive = array();
if (has_capability('mod/quiz:preview', $context)) {
$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'));
}
$streditingquiz = get_string("editinga", "moodle", $strquiz);
$strupdate = get_string('updatethis', 'moodle', $strquiz);
- $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());
+ if ($contexts->have_one_edit_tab_cap('editq')) {
+ $row[] = new tabobject('editq', "$CFG->wwwroot/mod/quiz/edit.php?".$thispageurl->get_query_string(), $strquiz, $streditingquiz);
+ }
+ questionbank_navigation_tabs($row, $contexts, $thispageurl->get_query_string());
$tabs[] = $row;
+
}
print_tabs($tabs, $currenttab, $inactive, $activated);
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) {
global $CFG;
//If we've categories
if ($categories) {
//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
foreach ($categories as $cat) {
//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
$category = get_record ("question_categories","id",$cat->old_id);
//Print category contents
- fwrite($bf,full_tag("ID",4,false,$category->id));
- fwrite($bf,full_tag("NAME",4,false,$category->name));
- fwrite($bf,full_tag("INFO",4,false,$category->info));
- fwrite($bf,full_tag("PUBLISH",4,false,$category->publish));
- fwrite($bf,full_tag("STAMP",4,false,$category->stamp));
- fwrite($bf,full_tag("PARENT",4,false,$category->parent));
- fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder));
+ $status = $status && fwrite($bf,full_tag("ID",4,false,$category->id));
+ $status = $status && fwrite($bf,full_tag("NAME",4,false,$category->name));
+ $status = $status && fwrite($bf,full_tag("INFO",4,false,$category->info));
+ $status = $status && backup_question_category_context($bf, $category->contextid, $preferences->backup_course);
+ $status = $status && fwrite($bf,full_tag("STAMP",4,false,$category->stamp));
+ $status = $status && fwrite($bf,full_tag("PARENT",4,false,$category->parent));
+ $status = $status && fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder));
//Now, backup their questions
- $status = backup_question($bf,$preferences,$category->id);
+ $status = $status && backup_question($bf,$preferences,$category->id);
//End category
- $status = fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true));
+ $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true));
}
//Write end tag
- $status = fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true));
+ $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true));
}
return $status;
// 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
// 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 ($questions) {
//Write start tag
- $status = fwrite ($bf,start_tag("QUESTIONS",$level,true));
+ $status = $status && fwrite ($bf,start_tag("QUESTIONS",$level,true));
$counter = 0;
//Iterate over each question
foreach ($questions as $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
fwrite ($bf,full_tag("ID",$level + 2,false,$question->id));
fwrite ($bf,full_tag("PARENT",$level + 2,false,$question->parent));
fwrite ($bf,full_tag("STAMP",$level + 2,false,$question->stamp));
fwrite ($bf,full_tag("VERSION",$level + 2,false,$question->version));
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
- $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
- $status = fwrite ($bf,end_tag("QUESTION",$level + 1,true));
+ $status = $status && fwrite ($bf,end_tag("QUESTION",$level + 1,true));
//Do some output
$counter++;
if ($counter % 10 == 0) {
}
}
//Write end tag
- $status = fwrite ($bf,end_tag("QUESTIONS",$level,true));
+ $status = $status && fwrite ($bf,end_tag("QUESTIONS",$level,true));
}
return $status;
}
$answers = get_records("question_answers","question",$question,"id");
//If there are answers
if ($answers) {
- $status = fwrite ($bf,start_tag("ANSWERS",$level,true));
+ $status = $status && fwrite ($bf,start_tag("ANSWERS",$level,true));
//Iterate over each 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
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("FRACTION",$level + 2,false,$answer->fraction));
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;
}
$numerical_units = get_records("question_numerical_units","question",$question,"id");
//If there are numericals_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
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
fwrite ($bf,full_tag("MULTIPLIER",$level+2,false,$numerical_unit->multiplier));
fwrite ($bf,full_tag("UNIT",$level+2,false,$numerical_unit->unit));
//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;
fwrite ($bf,full_tag("OPTIONS",$level+2,false,$def->options));
fwrite ($bf,full_tag("ITEMCOUNT",$level+2,false,$def->itemcount));
//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
$status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITION",$level+1,true));
}
//If there are states
if ($question_states) {
//Write start tag
- $status = fwrite ($bf,start_tag("STATES",$level,true));
+ $status = $status && fwrite ($bf,start_tag("STATES",$level,true));
//Iterate over each state
foreach ($question_states as $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
fwrite ($bf,full_tag("ID",$level + 2,false,$state->id));
fwrite ($bf,full_tag("QUESTION",$level + 2,false,$state->question));
fwrite ($bf,full_tag("RAW_GRADE",$level + 2,false,$state->raw_grade));
fwrite ($bf,full_tag("PENALTY",$level + 2,false,$state->penalty));
//End state
- $status = fwrite ($bf,end_tag("STATE",$level + 1,true));
+ $status = $status && fwrite ($bf,end_tag("STATE",$level + 1,true));
}
//Write end tag
- $status = fwrite ($bf,end_tag("STATES",$level,true));
+ $status = $status && fwrite ($bf,end_tag("STATES",$level,true));
}
}
//If there are sessions
if ($question_sessions) {
//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
foreach ($question_sessions as $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
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("NEWEST",$level + 2,false,$newest_state->newest));
fwrite ($bf,full_tag("NEWGRADED",$level + 2,false,$newest_state->newgraded));
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
- $status = fwrite ($bf,end_tag("NEWEST_STATE",$level + 1,true));
+ $status = $status && fwrite ($bf,end_tag("NEWEST_STATE",$level + 1,true));
}
//Write end tag
- $status = fwrite ($bf,end_tag("NEWEST_STATES",$level,true));
+ $status = $status && fwrite ($bf,end_tag("NEWEST_STATES",$level,true));
}
return $status;
}
global $CFG;
- return get_records_sql ("SELECT q.id, q.category
- FROM {$CFG->prefix}backup_ids a,
- {$CFG->prefix}question q
- WHERE a.backup_code = '$backup_unique_code' AND
- q.category = a.old_id AND
- a.table_name = 'question_categories'");
+ return get_records_sql ("SELECT old_id, backup_code
+ FROM {$CFG->prefix}backup_ids
+ WHERE backup_code = '$backup_unique_code' AND
+ table_name = 'question'");
}
//Delete category ids from backup_ids table
- function delete_category_ids ($backup_unique_code) {
+ function delete_ids ($backup_unique_code, $tablename) {
global $CFG;
- $status = true;
$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;
}
-
?>
*/
require_once("../config.php");
- require_once("editlib.php");
- require_once("category_class.php");
+ require_once($CFG->dirroot."/question/editlib.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
$param = new stdClass();
$param->moveup = optional_param('moveup', 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->right = optional_param('right', 0, PARAM_INT);
- $param->hide = optional_param('hide', 0, PARAM_INT);
$param->delete = optional_param('delete', 0, PARAM_INT);
$param->confirm = optional_param('confirm', 0, PARAM_INT);
$param->cancel = optional_param('cancel', '', PARAM_ALPHA);
$param->move = optional_param('move', 0, PARAM_INT);
$param->moveto = optional_param('moveto', 0, PARAM_INT);
- $param->publish = optional_param('publish', 0, PARAM_INT);
- $param->addcategory = optional_param('addcategory', '', PARAM_NOTAGS);
$param->edit = optional_param('edit', 0, PARAM_INT);
- $param->updateid = optional_param('updateid', 0, PARAM_INT);
-
- if (! $course = get_record("course", "id", $courseid)) {
- error("Course ID is incorrect");
- }
-
- $context = get_context_instance(CONTEXT_COURSE, $courseid);
- require_capability('moodle/question:managecategory', $context);
-
- $qcobject = new question_category_object($pagevars['cpage'], $thispageurl);
+ $qcobject = new question_category_object($pagevars['cpage'], $thispageurl, $contexts->having_one_edit_tab_cap('categories'), $param->edit, $pagevars['cat'], $param->delete,
+ $contexts->having_cap('moodle/question:add'));
$streditingcategories = get_string('editcategories', 'quiz');
- if ($qcobject->editlist->process_actions($param->left, $param->right, $param->moveup, $param->movedown)) {
- //processing of these actions is handled in the method and page redirects.
- } else if ($cm!==null) {
+ if ($param->left || $param->right || $param->moveup || $param->movedown|| $param->moveupcontext || $param->movedowncontext){
+ confirm_sesskey();
+ 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
- $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))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
+ ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: "";
$navlinks = array();
- $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname),
- 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id",
+ $navlinks[] = array('name' => get_string('modulenameplural', $cm->modname),
+ 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id",
'type' => 'activity');
- $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}",
'type' => 'title');
+ } else {
+ // Print basic page layout.
+ $strupdatemodule = '';
+ $navlinks = array();
+ }
+
+ if (!$param->edit){
$navlinks[] = array('name' => $streditingcategories, 'link' => '', 'type' => 'title');
- $navigation = build_navigation($navlinks);
- print_header_simple($streditingcategories, '', $navigation, "", "", true, $strupdatemodule);
+ } else {
+ $navlinks[] = array('name' => $streditingcategories, 'link' => $thispageurl->out(), 'type' => 'title');
+ $navlinks[] = array('name' => get_string('editingcategory', 'question'), 'link' => '', 'type' => 'title');
+ }
+ $navigation = build_navigation($navlinks);
+ print_header_simple($streditingcategories, '', $navigation, "", "", true, $strupdatemodule);
+ // print tabs
+ if ($cm!==null) {
$currenttab = 'edit';
$mode = 'categories';
${$cm->modname} = $module;
include($CFG->dirroot."/mod/{$cm->modname}/tabs.php");
} 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';
+ $context = $contexts->lowest();
include('tabs.php');
}
- // Process actions.
- if (isset($_REQUEST['sesskey']) and confirm_sesskey()) { // sesskey must be ok
- if (!empty($param->delete) and empty($param->cancel)) {
- if (!empty($param->confirm)) {
- /// 'confirm' is the category to move existing questions to
- $qcobject->delete_category($param->delete, $param->confirm);
- } else {
- $qcobject->delete_category($param->delete);
- }
- } else if (!empty($param->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 UI
+ if (!empty($param->edit)) {
+ $qcobject->edit_single_category($param->edit);
+ } else if ($questionstomove){
+ $qcobject->display_move_form($questionstomove, $category);
+ } else {
+ // display the user interface
+ $qcobject->display_user_interface();
}
-
- // display the user interface
- $qcobject->display_user_interface();
-
- print_footer($course);
+ print_footer($COURSE);
?>
*/
// 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->dirroot/question/category_form.php");
+require_once('move_form.php');
class question_category_list extends moodle_list {
var $table = "question_categories";
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() {
- global $COURSE, $CFG;
- $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;
+ $this->records = get_categories_for_contexts($this->context->id, $this->sortby);
+ }
+ 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 {
+ 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()){
global $CFG;
$pixpath = $CFG->pixpath;
$str = $extraargs['str'];
$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
-
-
- $item = '<a ' . $linkcss . ' title="' . $str->edit. '" href="'.$this->parentlist->pageurl->out_action(array('edit'=>$this->id)).'">
- <img src="' . $pixpath . '/t/edit.gif" class="iconsmall"
- alt="' .$str->edit. '" /> ' . $category->name . '('.$category->questioncount.')'. '</a>';
+ $questionbankurl = "{$CFG->wwwroot}/question/edit.php?".
+ $this->parentlist->pageurl->get_query_string(array('category'=>"$category->id,$category->contextid"));
+ $catediturl = $this->parentlist->pageurl->out(false, array('edit'=>$this->id));
+ $item = "<a title=\"{$str->edit}\" href=\"$catediturl\">".$category->name ."</a> <a title=\"$editqestions\" href=\"$questionbankurl\">".'('.$category->questioncount.')</a>';
$item .= ' '. $category->info;
- if (!empty($category->publish)) {
- $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) {
+ if (count($this->parentlist->records)!=1){ // don't allow delete if this is the last category in this context.
$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> ';
}
var $str;
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 $tab;
var $tabsize = 3;
- var $categories;
- var $categorystrings;
- var $defaultcategory;
//------------------------------------------------------
/**
* @var moodle_url Object representing url for this page
*/
var $pageurl;
+ /**
+ * @var question_category_edit_form Object representing form for adding / editing categories.
+ */
+ var $catform;
/**
* Constructor
*
* 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;
$this->tab = str_repeat(' ', $this->tabsize);
$this->str->page = get_string('page');
$this->pixpath = $CFG->pixpath;
- $this->editlist = new question_category_list('ul', '', true, $pageurl, $page, 'cpage', QUESTION_PAGE_LENGTH);
-
$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
*
function display_user_interface() {
/// Interface for editing existing categories
- print_heading_with_help($this->str->editcategories, 'categories', 'quiz');
- $this->output_edit_list();
+ $this->output_edit_lists();
echo '<br />';
/// Interface for adding a new category:
- print_heading_with_help($this->str->addcategory, 'categories_edit', 'quiz');
$this->output_new_table();
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
*/
function output_new_table() {
- global $USER, $COURSE;
- $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>';
+ $this->catform->display();
}
* $this->initialize() must have already been called
*
*/
- function output_edit_list() {
- print_box_start('boxwidthwide boxaligncenter generalbox');
- echo $this->editlist->to_html(0, array('str'=>$this->str,
- 'defaultcategory' => $this->defaultcategory));
- print_box_end();
- echo $this->editlist->display_page_numbers();
-
- }
+ 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');
+ echo $listhtml;
+ print_box_end();
+ }
+ }
+ echo $list->display_page_numbers();
+ }
function edit_single_category($categoryid) {
/// Interface for adding a new category
- global $CFG, $USER, $COURSE;
-
+ global $COURSE;
/// Interface for editing existing categories
if ($category = get_record("question_categories", "id", $categoryid)) {
- print_heading_with_help($this->str->edit, 'categories_edit', 'quiz');
- $this->output_edit_single_table($category);
- echo '<div class="centerpara">';
- print_single_button($CFG->wwwroot . '/question/category.php',
- $this->pageurl->params, $this->str->cancel);
- echo '</div>';
- print_footer($COURSE);
- exit;
- } else {
- error("Category $categoryid not found", "category.php?id={$COURSE->id}");
- }
- }
- /**
- * Outputs a table to allow editing of an existing category
- *
- * @param object category
- * @param int page current page
- */
- function output_edit_single_table($category) {
- 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);
- }
+ $category->parent = "$category->parent,$category->contextid";
+ $category->submitbutton = get_string('savechanges');
+ $category->categoryheader = $this->str->edit;
+ $this->catform->set_data($category);
+ $this->catform->display();
+ } else {
+ error("Category $categoryid not found");
}
- return $categorystrings;
}
* Deletes an existing question category
*
* @param int deletecat id of category to delete
- * @param int destcategoryid id of category which will inherit the orphans of deletecat
*/
- function delete_category($deletecat, $destcategoryid = null) {
- global $USER, $COURSE;
-
- if (!$category = get_record("question_categories", "id", $deletecat)) { // security
- error("No such category $deletecat!", "category.php?id={$COURSE->id}");
- }
-
- 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;
- }
+ function delete_category($categoryid) {
+ global $CFG;
+ question_can_delete_cat($categoryid);
+ if (!$category = get_record("question_categories", "id", $categoryid)) { // security
+ error("No such category $cat!", $this->pageurl->out());
}
-
/// Send the children categories to live with their grandparent
- if ($childcats = get_records("question_categories", "parent", $category->id)) {
- foreach ($childcats as $childcat) {
- if (! set_field("question_categories", "parent", $category->parent, "id", $childcat->id)) {
- error("Could not update a child category!", "category.php?id={$COURSE->id}");
- }
- }
+ if (!set_field("question_categories", "parent", $category->parent, "parent", $category->id)) {
+ error("Could not update a child category!", $this->pageurl->out());
}
/// Finally delete the category itself
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);
+ }
+ 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();
+ }
-
- /**
- * 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 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
*
- * @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)) {
- notify(get_string('categorynamecantbeblank', 'quiz'), 'notifyproblem');
- return false;
+ error(get_string('categorynamecantbeblank', 'quiz'));
}
+ 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) {
- // first check that the parent category is in the correct course
- if(!(get_field('question_categories', 'course', 'id', $newparent) == $newcourse)) {
- return false;
+ if ($parentid) {
+ if(!(get_field('question_categories', 'contextid', 'id', $parentid) == $contextid)) {
+ error("Could not insert the new question category '$newcategory' illegal contextid '$contextid'.");
}
}
- $cat = NULL;
- $cat->parent = $newparent;
+ $cat = new object();
+ $cat->parent = $parentid;
+ $cat->contextid = $contextid;
$cat->name = $newcategory;
$cat->info = $newinfo;
- $cat->publish = $newpublish;
- $cat->course = $newcourse;
$cat->sortorder = 999;
$cat->stamp = make_unique_id_code();
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 {
- notify(get_string("categoryadded", "quiz", $newcategory), 'notifysuccess');
redirect($this->pageurl->out());//always redirect after successful action
}
}
/**
* 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) {
- if (empty($updatename)) {
- notify(get_string('categorynamecantbeblank', 'quiz'), 'notifyproblem');
- return false;
+ function update_category($updateid, $newparent, $newname, $newinfo) {
+ global $CFG, $QTYPES;
+ if (empty($newname)) {
+ 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->id = $updateid;
- $cat->parent = $updateparent;
- $cat->name = $updatename;
- $cat->info = $updateinfo;
- $cat->publish = $updatepublish;
- if (!update_record("question_categories", $cat)) {
- error("Could not update the category '$updatename'", "category.php?id={$courseid}");
+ $cat->name = $newname;
+ $cat->info = $newinfo;
+ //never move category where it is the default
+ 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)) {
+ error("Could not update the category '$newname'", $this->pageurl->out());
+ } else {
+ 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 {
- notify(get_string("categoryupdated", 'quiz'), 'notifysuccess');
- redirect($this->pageurl->out());
+ 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>';
+ }
+ }
+
+
+
+
}
?>
require_once("../config.php");
require_once("editlib.php");
- list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup();
-
- if (! $course = get_record("course", "id", $courseid)) {
- error("This course doesn't exist");
- }
- $context = get_context_instance(CONTEXT_COURSE, $courseid);
-
+ list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('questions');
+
+ question_showbank_actions($thispageurl, $cm);
+ $context = $contexts->lowest();
$streditingquestions = get_string('editquestions', "quiz");
if ($cm!==null) {
- $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))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
+ ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: "";
$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();
$navlinks[] = array('name' => $streditingquestions, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($streditingquestions, '', $navigation);
-
+
// print tabs
$currenttab = 'questions';
include('tabs.php');
}
-
-
+
+
echo '<table class="boxaligncenter" border="0" cellpadding="2" cellspacing="0">';
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']);
echo '</td></tr>';
echo '</table>';
- print_footer($course);
+ print_footer($COURSE);
?>
/**
* 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
* rewritten by members of the Serving Mathematics project
}
$modrec->instance = $modrec->id;
$modrec->cmid = $cmrec->id;
-
+
return array($modrec, $cmrec);
}
/**
}
/**
-* 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
-* exist yet then one is created.
-* @return object The default category
-* @param integer $courseid The id of the course whose default category is wanted
+* @param array $contexts The context objects for this context and all parent contexts.
+* @return object The default category - the category in the course context
*/
-function get_default_question_category($courseid) {
+function question_make_default_categories($contexts) {
// If it already exists, just return it.
- if ($category = get_records_select("question_categories", "course = '$courseid' AND parent = '0'", 'id', '*', '', 1)) {
- return reset($category);
- }
-
- // Otherwise, we need to make one
- $category = new stdClass;
- $category->name = get_string("default", "quiz");
- $category->info = get_string("defaultinfo", "quiz");
- $category->course = $courseid;
- $category->parent = 0;
- $category->sortorder = 999; // By default, all categories get this number, and are sorted alphabetically.
- $category->publish = 0;
- $category->stamp = make_unique_id_code();
-
- if (!$category->id = insert_record("question_categories", $category)) {
- notify("Error creating a default category!");
- return false;
+ foreach ($contexts as $key => $context) {
+ 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
+ $category = new stdClass;
+ $contextname = print_context_name($context, false, true);
+ $category->name = addslashes(get_string('defaultfor', 'question', $contextname));
+ $category->info = addslashes(get_string('defaultinfofor', 'question', $contextname));
+ $category->contextid = $context->id;
+ $category->parent = 0;
+ $category->sortorder = 999; // By default, all categories get this number, and are sorted alphabetically.
+ $category->stamp = make_unique_id_code();
+ if (!$category->id = insert_record('question_categories', $category)) {
+ error('Error creating a default category for context '.print_context_name($context));
+ }
+ }
+ }
+ if ($context->contextlevel == CONTEXT_COURSE){
+ $toreturn = clone($category);
+ }
}
- return $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
*/
-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;
-/// Make sure the default category exists for this course
- get_default_question_category($course->id);
/// 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");
- $strshow = get_string("show", "quiz");
- $streditcats = get_string("editcategories", "quiz");
+ $strcategory = get_string('category', 'quiz');
+ $strshow = get_string('show', 'quiz');
+ $streditcats = get_string('editcategories', 'quiz');
- popup_form ("edit.php?".$pageurl->get_query_string()."&category=", $catmenu, "catmenu", $current, "", "", "", false, "self", "<strong>$strcategory</strong>");
+ popup_form ('edit.php?'.$pageurl->get_query_string().'&category=', $catmenu, 'catmenu', $current, '', '', '', false, 'self', "<strong>$strcategory</strong>");
echo '<form method="get" action="edit.php" id="displayoptions">';
echo "<fieldset class='invisiblefieldset'>";
* @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
*/
-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',
- $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");
$strquestion = get_string("question", "quiz");
$straddquestions = get_string("addquestions", "quiz");
$strquestionname = get_string("questionname", "quiz");
$strdelete = get_string("delete");
$stredit = get_string("edit");
+ $strmove = get_string('moveqtoanothercontext', 'question');
+ $strview = get_string("view");
$straction = get_string("action");
$strrestore = get_string('restore');
return;
}
- if (!$category = get_record('question_categories', 'id', $categoryid)) {
+ if (!$category = get_record('question_categories', 'id', $categoryid, 'contextid', $contextid)) {
notify('Category not found!');
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') {
$quizid = $cm->instance;
} else {
$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">';
$formatoptions = new stdClass;
$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>';
- // check if editing questions in this category is allowed
- if ($canedit) {
+ if ($canadd) {
echo '<td valign="top" align="right">';
- $returnurl = urlencode($pageurl->out());
- $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().'&qtype=', $QTYPE_MENU, "addquestion", "", "choose", "", "", false, "self", "<strong>$strcreatenewquestion</strong>");
+ popup_form ($questionurl->out(false, array('category' => $category->id)).'&qtype=', $qtypemenu, "addquestion", "", "choose", "", "", false, "self", "<strong>$strcreatenewquestion</strong>");
echo '</td><td valign="top" align="right">';
helpbutton("questiontypes", $strcreatenewquestion, "quiz");
echo '</td>';
}
else {
echo '<td>';
- print_string("publishedit","quiz");
+ print_string('nopermissionadd', 'question');
echo '</td>';
}
print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage');
+ echo question_sort_options($pageurl, $sortorder);
+
+
echo '<form method="post" action="edit.php">';
echo '<fieldset class="invisiblefieldset" style="display: block;">';
echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
echo $pageurl->hidden_params_out(array('qsortorder'));
echo '<table id="categoryquestions" style="width: 100%"><tr>';
echo "<th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$straction</th>";
-
- $sortoptions = array('alpha' => get_string("sortalpha", "quiz"),
- '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>
+
+ echo "<th style=\"white-space:nowrap; text-align: left;\" class=\"header\" scope=\"col\">$strquestionname</th>
<th style=\"white-space:nowrap; text-align: right;\" class=\"header\" scope=\"col\">$strtype</th>";
echo "</tr>\n";
foreach ($questions as $question) {
if ($textclass) {
$textclass = 'class="' . $textclass . '"';
}
-
+
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')) {
- echo module_specific_actions($pageurl, $question->id, $cm->id);
+ echo module_specific_actions($pageurl, $question->id, $cm->id, $canuseq);
}
-
+
// preview
- link_to_popup_window('/question/preview.php?id=' . $question->id . '&quizid=' . $quizid, 'questionpreview',
- "<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />",
- 0, 0, $strpreview, QUESTION_PREVIEW_POPUP_OPTIONS);
-
+ if ($canuseq) {
+ link_to_popup_window('/question/preview.php?id=' . $question->id . '&quizid=' . $quizid, 'questionpreview',
+ "<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strpreview\" />",
+ 0, 0, $strpreview, QUESTION_PREVIEW_POPUP_OPTIONS);
+ }
// edit, hide, delete question, using question capabilities, not quiz capabilieies
- if ($canedit) {
- $questionparams = (($cm !== null)? array('cmid' => $cm->id) : array()) +
- (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
+ if (question_has_capability_on($question, 'edit', $question->category) || question_has_capability_on($question, 'move', $question->category)) {
+ echo "<a title=\"$stredit\" href=\"".$questionurl->out(false, array('id'=>$question->id))."\"><img
src=\"$CFG->pixpath/t/edit.gif\" 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> ";
+ }
+
+ 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> ";
+ }
+
+ if (question_has_capability_on($question, 'edit', $question->category)) {
// hide-feature
if($question->hidden) {
echo "<a title=\"$strrestore\" href=\"edit.php?".$pageurl->get_query_string()."&unhide=$question->id&sesskey=$USER->sesskey\"><img
src=\"$CFG->pixpath/t/delete.gif\" alt=\"$strdelete\" /></a>";
}
}
- echo " <input title=\"$strselect\" type=\"checkbox\" name=\"q$question->id\" value=\"1\" />";
+ if ($caneditall || $canmoveall || $canuseall){
+ echo " <input title=\"$strselect\" type=\"checkbox\" name=\"q$question->id\" value=\"1\" />";
+ }
echo "</td>\n";
echo "<td $nameclass>" . format_string($question->name) . "</td>\n";
$formatoptions->noclean = true;
$formatoptions->para = false;
echo format_text($question->questiontext, $question->questiontextformat,
- $formatoptions, $course->id);
+ $formatoptions, $COURSE->id);
echo "</td></tr>\n";
}
}
echo "</table>\n";
- $paging = print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage',
- false, true);
+ $paging = print_paging_bar($totalnumber, $page, $perpage, $pageurl, 'qpage', false, true);
if ($totalnumber > 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>';
$paging = substr($paging, 0, strrpos($paging, '</div>'));
$paging .= "<br />$showall</div>";
} else {
- $paging = "<div class='paging'>$showall</div>";
+ $paging = "<div class='paging'>$showall</div>";
}
}
echo $paging;
- echo '<table class="quiz-edit-selected"><tr><td colspan="2">';
- echo '<a href="javascript:select_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectall.'</a> /'.
- ' <a href="javascript:deselect_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectnone.'</a>'.
- '</td><td align="right"><b> '.get_string('withselected', 'quiz').':</b></td></tr><tr><td>';
+ if ($caneditall || $canmoveall || $canuseall){
+ echo '<a href="javascript:select_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectall.'</a> /'.
+ ' <a href="javascript:deselect_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectnone.'</a>';
+ echo '<br />';
+ echo '<strong> '.get_string('withselected', 'quiz').':</strong><br />';
- if (function_exists('module_specific_buttons')) {
- echo module_specific_buttons($cm->id);
- }
- // print delete and move selected question
- if ($canedit) {
- echo '<input type="submit" name="deleteselected" value="'.$strdelete."\" /></td><td>\n";
- echo '<input type="submit" name="move" value="'.get_string('moveto', 'quiz')."\" />\n";
- question_category_select_menu($course->id, false, true, $category->id);
- }
- echo "</td></tr></table>";
+ if (function_exists('module_specific_buttons')) {
+ echo module_specific_buttons($cm->id);
+ }
+ // print delete and move selected question
+ if ($caneditall) {
+ echo '<input type="submit" name="deleteselected" value="'.$strdelete."\" />\n";
+ }
+ 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");
+ }
- if (function_exists('module_specific_controls')) {
- echo module_specific_controls($totalnumber, $recurse, $category->id, $cm->id);
+ if (function_exists('module_specific_controls') && $canuseall) {
+ echo module_specific_controls($totalnumber, $recurse, $category, $cm->id);
+ }
}
echo '</fieldset>';
echo "</form>\n";
}
-/**
- * Shows the question bank editing interface.
- *
- * The function also processes a number of actions:
- *
- * Actions affecting the question pool:
- * move Moves a question to a different category
- * deleteselected Deletes the selected questions from the category
- * Other actions:
- * category Chooses the category
- * displayoptions Sets display options
- *
- * @author Martin Dougiamas and many others. This has recently been extensively
- * rewritten by Gustav Delius and other members of the Serving Mathematics project
- * {@link http://maths.york.ac.uk/serving_maths}
- * @param moodle_url $pageurl object representing this pages url.
- */
-function question_showbank($pageurl, $cm, $page, $perpage, $sortorder, $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext){
- global $COURSE, $USER;
+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;
+}
-/// Now, check for commands on this page and modify variables as necessary
+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
- $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());
+ $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];
- if (!set_field('question', 'category', $tocategory->id, 'id', $key)) {
- error('Could not update category field');
+ $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 ($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)) {
- error('Was not able to hide question');
+ question_require_capability_on($questionid, 'edit');
+ 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
- $rawquestions = $_REQUEST;
- $questionlist = ''; // comma separated list of ids of questions to be deleted
- $questionnames = ''; // string with names of questions separated by <br /> with
- // an asterix in front of those that are in use
- $inuse = false; // set to true if at least one of the questions is in use
- foreach ($rawquestions as $key => $value) { // Parse input for question ids
- if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
- $key = $matches[1]; $questionlist .= $key.',';
- if (record_exists('quiz_question_instances', 'question', $key) or
- record_exists('question_states', 'originalquestion', $key)) {
- $questionnames .= '* ';
- $inuse = true;
- }
- $questionnames .= get_field('question', 'name', 'id', $key).'<br />';
- }
- }
- if (!$questionlist) { // no questions were selected
- redirect($pageurl->out());
- }
- $questionlist = rtrim($questionlist, ',');
- // Add an explanation about questions in use
- if ($inuse) {
- $questionnames .= '<br />'.get_string('questionsinuse', 'quiz');
- }
- notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames),
- $pageurl->out(), $pageurl->out(),
- array('sesskey' => $USER->sesskey, 'deleteselected' => $questionlist, 'confirm' => md5($questionlist)),
- NULL, 'post', 'get');
-
- echo '</td></tr>';
- echo '</table>';
- print_footer($COURSE);
- exit;
}
}
// 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.
+ *
+ * The function also processes a number of actions:
+ *
+ * Actions affecting the question pool:
+ * move Moves a question to a different category
+ * deleteselected Deletes the selected questions from the category
+ * Other actions:
+ * category Chooses the category
+ * displayoptions Sets display options
+ *
+ * @author Martin Dougiamas and many others. This has recently been extensively
+ * rewritten by Gustav Delius and other members of the Serving Mathematics project
+ * {@link http://maths.york.ac.uk/serving_maths}
+ * @param moodle_url $pageurl object representing this pages url.
+ */
+function question_showbank($tabname, $contexts, $pageurl, $cm, $page, $perpage, $sortorder, $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext){
+ global $COURSE;
+
+ if (isset($_REQUEST['deleteselected'])){ // teacher still has to confirm
+ // make a list of all the questions that are selected
+ $rawquestions = $_REQUEST;
+ $questionlist = ''; // comma separated list of ids of questions to be deleted
+ $questionnames = ''; // string with names of questions separated by <br /> with
+ // an asterix in front of those that are in use
+ $inuse = false; // set to true if at least one of the questions is in use
+ foreach ($rawquestions as $key => $value) { // Parse input for question ids
+ if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
+ $key = $matches[1]; $questionlist .= $key.',';
+ question_require_capability_on($key, 'edit');
+ if (record_exists('quiz_question_instances', 'question', $key) or
+ record_exists('question_states', 'originalquestion', $key)) {
+ $questionnames .= '* ';
+ $inuse = true;
+ }
+ $questionnames .= get_field('question', 'name', 'id', $key).'<br />';
+ }
+ }
+ if (!$questionlist) { // no questions were selected
+ redirect($pageurl->out());
+ }
+ $questionlist = rtrim($questionlist, ',');
+
+ // Add an explanation about questions in use
+ if ($inuse) {
+ $questionnames .= '<br />'.get_string('questionsinuse', 'quiz');
+ }
+ notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames),
+ $pageurl->out_action(array('deleteselected'=>$questionlist, 'confirm'=>md5($questionlist))),
+ $pageurl->out_action());
+
+ echo '</td></tr>';
+ echo '</table>';
+ print_footer($COURSE);
+ exit;
+ }
+
// starts with category selection form
print_box_start('generalbox questionbank');
print_heading(get_string('questionbank', 'question'), '', 2);
- question_category_form($COURSE, $pageurl, $cat, $recurse,
- $showhidden, $showquestiontext);
-
+ question_category_form($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, $recurse, $showhidden, $showquestiontext);
+
// continues with list of questions
- question_list($COURSE, $pageurl, $cat, isset($cm) ? $cm : null,
- $recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded,
- $showquestiontext);
+ question_list($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, isset($cm) ? $cm : null,
+ $recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded, $showquestiontext,
+ $contexts->having_cap('moodle/question:add'));
print_box_end();
}
/**
* 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 $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){
- global $COURSE;
- //$thispageurl is used to construct urls for all question edit pages we link to from this page. It contains an array
+function question_edit_setup($edittab, $requirecmid = false, $requirecourseid = true){
+ 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
//of parameters that are passed from page to page.
$thispageurl = new moodle_url();
if ($requirecmid){
$cmid = optional_param('cmid', 0, PARAM_INT);
}
if ($cmid){
- list($module, $cm) = get_module_from_cmid($cmid);
+ list($module, $cm) = get_module_from_cmid($cmid);
$courseid = $cm->course;
$thispageurl->params(compact('cmid'));
+ require_login($courseid, false, $cm);
+ $thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
} else {
$module = null;
$cm = null;
}
if ($courseid){
$thispageurl->params(compact('courseid'));
+ require_login($courseid, false);
+ $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
+ } else {
+ $thiscontext = null;
}
}
- require_login($courseid, false);
-
-
+
+ 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);
-
+
//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.
- $pagevars['cat'] = optional_param('cat', 0, PARAM_INT);
- if ($category = optional_param('category', 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_SEQUENCE)){
$pagevars['cat'] = $category;
$pagevars['qpage'] = 0;
}
} else {
$pagevars['qperpage'] = DEFAULT_QUESTIONS_PER_PAGE;
}
-
+
$sortoptions = array('alpha' => 'name, qtype ASC',
'typealpha' => 'qtype, name ASC',
'age' => 'id ASC');
} else {
$pagevars['qsortorderdecoded'] = $sortoptions['typealpha'];
$pagevars['qsortorder'] = 'typealpha';
- }
-
+ }
- if (empty($pagevars['cat']) or !count_records_select("question_categories", "id = '".$pagevars['cat']."' AND (course = '{$COURSE->id}' OR publish = '1')")) {
- $category = get_default_question_category($COURSE->id);
- $pagevars['cat'] = $category->id;
- $thispageurl->param('cat', $category->id);
+ $defaultcategory = question_make_default_categories($contexts->all());
+
+ $contextlistarr = array();
+ foreach ($contexts->having_one_edit_tab_cap($edittab) as $context){
+ $contextlistarr[] = "'$context->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) {
} else {
$pagevars['recurse'] = 1;
}
-
+
if(($showhidden = optional_param('showhidden', -1, PARAM_BOOL)) != -1) {
$pagevars['showhidden'] = $showhidden;
$thispageurl->param('showhidden', $showhidden);
} else {
$pagevars['showhidden'] = 0;
}
-
+
if(($showquestiontext = optional_param('showquestiontext', -1, PARAM_BOOL)) != -1) {
$pagevars['showquestiontext'] = $showquestiontext;
$thispageurl->param('showquestiontext', $showquestiontext);
} else {
$pagevars['showquestiontext'] = 0;
}
-
+
//category list page
$pagevars['cpage'] = optional_param('cpage', 1, PARAM_INT);
if ($pagevars['cpage'] != 1){
$thispageurl->param('cpage', $pagevars['cpage']);
}
-
- 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();
+ }
}
-?>
+?>
\ No newline at end of file
*/
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
$txt = new object;
- $txt->category = get_string('category','quiz');
- $txt->download = get_string('download','quiz');
- $txt->downloadextra = get_string('downloadextra','quiz');
- $txt->exporterror = get_string('exporterror','quiz');
- $txt->exportname = get_string('exportname','quiz');
+ $txt->category = get_string('category', 'quiz');
+ $txt->download = get_string('download', 'quiz');
+ $txt->downloadextra = get_string('downloadextra', 'quiz');
+ $txt->exporterror = get_string('exporterror', 'quiz');
+ $txt->exportname = get_string('exportname', 'quiz');
$txt->exportquestions = get_string('exportquestions', 'quiz');
- $txt->fileformat = get_string('fileformat','quiz');
- $txt->exportcategory = get_string('exportcategory','quiz');
- $txt->modulename = get_string('modulename','quiz');
- $txt->modulenameplural = get_string('modulenameplural','quiz');
- $txt->tofile = get_string('tofile','quiz');
+ $txt->fileformat = get_string('fileformat', 'quiz');
+ $txt->exportcategory = get_string('exportcategory', 'quiz');
+ $txt->modulename = get_string('modulename', 'quiz');
+ $txt->modulenameplural = get_string('modulenameplural', '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
if (empty($categoryid)) {
$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
- make_upload_directory( "$course->id" );
-
- // check category is valid
- $validcats = question_category_options( $course->id, true, false );
- if (!array_key_exists( $categoryid, $validcats)) {
- print_error( 'invalidcategory','quiz' );
+ make_upload_directory("$COURSE->id");
+ list($catid, $catcontext) = explode(',', $pagevars['cat']);
+ if (!$category = get_record("question_categories", "id", $catid, 'contextid', $catcontext)) {
+ print_error('nocategory','quiz');
}
/// Header
if ($cm!==null) {
- $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))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
+ ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: "";
$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();
$navlinks[] = array('name' => $txt->exportquestions, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($txt->exportquestions, '', $navigation);
// print tabs
$currenttab = 'export';
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 (! is_readable("format/$format/format.php")) {
- error( "Format not known ($format)" ); }
+ if ($from_form = $export_form->get_data()) { /// Filename
+
+
+ if (! is_readable("format/$from_form->format/format.php")) {
+ error("Format not known ($from_form->format)");
+ }
// load parent class for import/export
- require("format.php");
+ require_once("format.php");
// 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->setContexts($contexts->having_one_edit_tab_cap('export'));
+ $qformat->setCategory($category);
+ $qformat->setCourse($COURSE);
- $qformat->setCategory( $category );
- $qformat->setCourse( $course );
- $qformat->setFilename( $exportfilename );
- $qformat->setCattofile( $cattofile );
+ if (empty($from_form->exportfilename)) {
+ $from_form->exportfilename = default_export_filename($COURSE, $category);
+ }
+ $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
- error( $txt->exporterror, $thispageurl->out());
+ error($txt->exporterror, $thispageurl->out());
}
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
- error( $txt->exporterror, $thispageurl->out());
+ error($txt->exporterror, $thispageurl->out());
}
echo "<hr />";
// link to download the finished file
$file_ext = $qformat->export_file_extension();
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 {
- $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\"><font size=\"-1\">$txt->downloadextra</font></div></p>";
print_continue("edit.php?".$thispageurl->get_query_string());
- print_footer($course);
+ print_footer($COURSE);
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_simple_box_start('center');
-?>
- <form enctype="multipart/form-data" method="post" action="export.php">
- <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> </td>
- </tr>
- </table>
- </fieldset>
- </form>
- <?php
-
- print_simple_box_end();
- print_footer($course);
+ $export_form->display();
+
+ print_footer($COURSE);
?>
-<?php // $Id$
+<?php // $Id$
/**
* Base class for question import and export formats.
*
var $filename = '';
var $matchgrades = 'error';
var $catfromfile = 0;
+ var $contextfromfile = 0;
var $cattofile = 0;
+ var $contexttofile = 0;
var $questionids = array();
var $importerrors = 0;
var $stoponerror = true;
+ var $translator = null;
+
// functions to indicate import/export functionality
// override to return true if implemented
function setCourse( $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
function setCatfromfile( $catfromfile ) {
$this->catfromfile = $catfromfile;
}
-
+
+ /**
+ * set contextfromfile
+ * @param bool $contextfromfile allow contexts embedded in import file
+ */
+ function setContextfromfile($contextfromfile) {
+ $this->contextfromfile = $contextfromfile;
+ }
+
/**
* set cattofile
* @param bool cattofile exports categories within export file
*/
function setCattofile( $cattofile ) {
$this->cattofile = $cattofile;
- }
+ }
+ /**
+ * set contexttofile
+ * @param bool cattofile exports categories within export file
+ */
+ function setContexttofile($contexttofile) {
+ $this->contexttofile = $contexttofile;
+ }
/**
* set stoponerror
$this->importerrors++;
}
- /**
+ /**
* Import for questiontype plugins
* Do not override.
* @param data mixed The segment of data containing the question
return $question;
}
}
- }
- return false;
+ }
+ return false;
}
/**
* @return boolean success
*/
function importprocess() {
+ global $USER;
// reset the timer in case file upload was slow
@set_time_limit();
// STAGE 1: Parse the file
notify( get_string('parsingquestions','quiz') );
-
+
if (! $lines = $this->readdata($this->filename)) {
notify( get_string('cannotread','quiz') );
return false;
foreach ($questions as $question) { // Process and store each question
- // reset the php timeout
+ // reset the php timeout
@set_time_limit();
// check for category modifiers
if ($this->catfromfile) {
// find/create category object
$catpath = $question->category;
- $newcategory = create_category_path( $catpath, '/', $this->course->id );
+ $newcategory = $this->create_category_path( $catpath, '/');
if (!empty($newcategory)) {
$this->category = $newcategory;
}
}
- continue;
+ continue;
}
$count++;
$question->category = $this->category->id;
$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)) {
error( get_string('cannotinsert','quiz') );
}
}
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
* @param string filename name of file
}
/**
- * Parses an array of lines into an array of questions,
- * where each item is a question object as defined by
- * readquestion(). Questions are defined as anything
+ * Parses an array of lines into an array of questions,
+ * where each item is a question object as defined by
+ * readquestion(). Questions are defined as anything
* between blank lines.
*
* If your format does not use blank lines as a delimiter
* @return array array of question objects
*/
function readquestions($lines) {
-
+
$questions = array();
$currentquestion = array();
* by import but are required db fields.
* This should not be overridden.
* @return object default question
- */
+ */
function defaultquestion() {
global $CFG;
-
+
$question = new stdClass();
- $question->shuffleanswers = $CFG->quiz_shuffleanswers;
+ $question->shuffleanswers = $CFG->quiz_shuffleanswers;
$question->defaultgrade = 1;
$question->image = "";
$question->usecase = 0;
}
/**
- * Given the data known to define a question in
- * this format, this function converts it into a question
+ * Given the data known to define a question in
+ * this format, this function converts it into a question
* object suitable for processing and insertion into Moodle.
*
* If your format does not use blank lines to delimit questions
check_dir_exists($destination, true, true );
// detect and fix any filename collision - get unique filename
- $newfiles = resolve_filename_collisions( $destination, array($file) );
+ $newfiles = resolve_filename_collisions( $destination, array($file) );
$newfile = $newfiles[0];
// convert and save file contents
* EXPORT FUNCTIONS
*******************/
- /**
+ /**
* Provide export functionality for plugin questiontypes
* Do not override
* @param name questiontype name
- * @param question object data to export
+ * @param question object data to export
* @param extra mixed any addition format specific data needed
* @return string the data to append to export or false if error (or unhandled)
*/
// results are first written into string (and then to a file)
// so create/initialize the string here
$expout = "";
-
+
// track which category questions are in
// if it changes we will record the category change in the output
// file if selected. 0 means that it will get printed before the 1st question
// iterate through questions
foreach($questions as $question) {
-
+
// do not export hidden questions
if (!empty($question->hidden)) {
continue;
if ($question->qtype==RANDOM) {
continue;
}
-
+
// check if we need to record category change
if ($this->cattofile) {
if ($question->category != $trackcategory) {
- $trackcategory = $question->category;
- $categoryname = get_category_path( $trackcategory );
-
+ $trackcategory = $question->category;
+ $categoryname = $this->get_category_path($trackcategory, '/', $this->contexttofile);
+
// create 'dummy' question for category export
$dummyquestion = new object;
$dummyquestion->qtype = 'category';
$dummyquestion->id = 0;
$dummyquestion->questiontextformat = '';
$expout .= $this->writequestion( $dummyquestion ) . "\n";
- }
- }
+ }
+ }
// export the question displaying message
$count++;
// final pre-process on exported data
$expout = $this->presave_process( $expout );
-
+
// write file
$filepath = $path."/".$this->filename . $this->export_file_extension();
if (!$fh=fopen($filepath,"w")) {
fclose($fh);
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
// if not overidden, then this is an error.
$formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
echo "<p>$formatnotimplemented</p>";
-
return NULL;
}
/**
- * get directory into which export is going
+ * get directory into which export is going
* @return string file path
*/
function question_get_export_dir() {
}
return format_text(stripslashes($question->questiontext), $format, $formatoptions);
}
+
+
}
?>
}
function importprocess($filename) {
- global $CFG,$strimportquestions,$form,$question_category,$category,$course,
- $hostname, $mdapath, $mdbpath;
+ global $CFG, $USER, $strimportquestions,$form,$question_category,$category,$course,
+ $hostname, $mdapath, $mdbpath;
if ((PHP_OS == "Linux") and isset($hostname)) {
$hostname = trim($hostname);
// test the ODBC socket server connection
echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$question->category = $this->category->id;
$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)) {
error("Could not insert new question!");
}
*/
require_once("../config.php");
- require_once("editlib.php" );
+ require_once("editlib.php");
require_once($CFG->libdir . '/uploadlib.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->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->importfilearea = get_string('importfilearea','quiz');
- $txt->importfileupload = get_string('importfileupload','quiz');
- $txt->importfromthisfile = get_string('importfromthisfile','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
- $matchgrades = array();
- $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
+ list($catid, $catcontext) = explode(',', $pagevars['cat']);
+ if (!$category = get_record("question_categories", "id", $catid)) {
print_error('nocategory','quiz');
}
- // check category is valid (against THIS courseid, before we change it)
- $validcats = question_category_options( $cmid, false, true );
- if (!array_key_exists( $params->category, $validcats )) {
- print_error( 'invalidcategory', 'quiz' );
- }
-
- $localcourseid = $cmid;
- $courseid = $category->course;
-
- if (!$course = get_record("course", "id", $courseid)) {
- error("Invalid course!");
+ //this page can be called without courseid or cmid in which case
+ //we get the context from the category object.
+ if ($contexts === null) { // need to get the course from the chosen category
+ $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);
}
- 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
- 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
//==========
if ($cm!==null) {
- $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))
+ $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))
: "";
$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();
$navlinks[] = array('name' => $txt->importquestions, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($txt->importquestions, '', $navigation);
// print tabs
$currenttab = 'import';
// file upload form sumitted
- if (!empty($format) and confirm_sesskey() ) {
+ if ($form = $import_form->get_data()) {
// file checks out ok
$fileisgood = false;
- // work out if this is an uploaded file
+ // work out if this is an uploaded file
// or one from the filesarea.
- if (!empty($params->choosefile)) {
- $importfile = "{$CFG->dataroot}/{$course->id}/{$params->choosefile}";
+ if (!empty($form->choosefile)) {
+ $importfile = "{$CFG->dataroot}/{$COURSE->id}/{$form->choosefile}";
if (file_exists($importfile)) {
$fileisgood = true;
- }
- else {
- notify($txt->uploadproblem);
+ } else {
+ error(get_string('uploadproblem', 'moodle', $form->choosefile));
}
} else {
// must be upload file
- if (empty($_FILES['newfile'])) {
- notify( $txt->uploadproblem );
- }
- else if ((!is_uploaded_file($_FILES['newfile']['tmp_name']) or $_FILES['newfile']['size'] == 0)) {
- notify( $txt->uploadproblem );
- }
- else {
- $importfile = $_FILES['newfile']['tmp_name'];
+ if (!$importfile = $import_form->get_importfile_name()) {
+ error(get_string('uploadproblem', 'moodle'));
+ }else {
$fileisgood = true;
}
}
// process if we are happy file is ok
- if ($fileisgood) {
+ if ($fileisgood) {
- if (! is_readable("format/$format/format.php")) {
- error( get_string('formatnotfound','quiz', $format) );
+ if (! is_readable("format/$form->format/format.php")) {
+ error(get_string('formatnotfound','quiz', $form->format));
}
- require("format.php"); // Parent class
- require("format/$format/format.php");
+ require_once("format.php"); // Parent class
+ require_once("format/$form->format/format.php");
- $classname = "qformat_$format";
+ $classname = "qformat_$form->format";
$qformat = new $classname();
// load data into class
- $qformat->setCategory( $category );
- $qformat->setCourse( $course );
- $qformat->setFilename( $importfile );
- $qformat->setMatchgrades( $params->matchgrades );
- $qformat->setCatfromfile( $catfromfile );
- $qformat->setStoponerror( $params->stoponerror );
+ $qformat->setCategory($category);
+ $qformat->setContexts($contexts->having_one_edit_tab_cap('import'));
+ $qformat->setCourse($COURSE);
+ $qformat->setFilename($importfile);
+ $qformat->setMatchgrades($form->matchgrades);
+ $qformat->setCatfromfile(!empty($form->catfromfile));
+ $qformat->setContextfromfile(!empty($form->contextfromfile));
+ $qformat->setStoponerror($form->stoponerror);
// Do anything before that we need to
- if (! $qformat->importpreprocess()) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ if (! $qformat->importpreprocess()) {
+ error($txt->importerror, $thispageurl->out());
}
// Process the uploaded file
- if (! $qformat->importprocess() ) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ if (! $qformat->importprocess()) {
+ error($txt->importerror, $thispageurl->out());
}
// In case anything needs to be done after
if (! $qformat->importpostprocess()) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ error($txt->importerror, $thispageurl->out());
}
echo "<hr />";
- print_continue("edit.php?".$thispageurl->get_query_string());
- print_footer($course);
+ print_continue("edit.php?".($thispageurl->get_query_string(array('category'=>"{$qformat->category->id},{$qformat->category->contextid}"))));
+ print_footer($COURSE);
exit;
}
}
- /// Print upload form
-
- // get list of available import formats
- $fileformatnames = get_import_export_formats( 'import' );
-
print_heading_with_help($txt->importquestions, "import", "quiz");
- /// Get all the existing categories now
- $catmenu = question_category_options($course->id, false, true);
-
- //==========
- // 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> </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> </td>
- <td><?php button_to_popup_window ("/files/index.php?id={$course->id}&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);
+ /// Print upload form
+ $import_form->display();
+ print_footer($COURSE);
?>
$continue = false;
}
- require_login();
- // this might break things in the future
- if (!isteacherinanycourse()) {
- error('This page is for teachers only');
- }
if (!$continue) {
// Start a new attempt; delete the old session
$url .= '&continue=1';
redirect($url);
}
-
+ // Load the question information
+ if (!$questions = get_records('question', 'id', $id)) {
+ 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));
}
- // Load the question information
- if (!$questions = get_records('question', 'id', $id)) {
- error('Could not load question');
- }
+
+
if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) {
$questions[$id]->maxgrade = $maxgrade;
} else {
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!");
}
- $quiz->course = $category->course;
+ if (isset($COURSE)){
+ $quiz->course = $COURSE->id;
+ }
// Load the question type specific information
if (!get_question_options($questions)) {
echo '<br />';
-
+
echo '<div class="controls">';
echo "<input type=\"hidden\" name=\"id\" value=\"$id\" />\n";
echo "<input type=\"hidden\" name=\"quizid\" value=\"$quizid\" />\n";
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/formslib.php');
-$returnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
-
// Read URL parameters telling us which question to edit.
$id = optional_param('id', 0, PARAM_INT); // question id
-$cmid = optional_param('cmid', 0, PARAM_INT);
$qtype = optional_param('qtype', '', PARAM_FILE);
$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);
+$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){
- list($module, $cm) = get_module_from_cmid($cmid);
-} else {
+ list($module, $cm) = get_module_from_cmid($cmid);
+ 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;
$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 (!$question = get_record('question', 'id', $id)) {
print_error('questiondoesnotexist', 'question', $returnurl);
if (!$category = get_record('question_categories', 'id', $question->category)) {
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.
if (!isset($QTYPES[$question->qtype])) {
print_error('unknownquestiontype', 'question', $returnurl, $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.
-if ($wizardnow!==''){
+if ($wizardnow!=='' && !$movecontext){
if (!method_exists($QTYPES[$question->qtype], 'next_wizard_form')){
print_error('missingimportantcode', 'question', $returnurl, 'wizard form definition');
} 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 {
- $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) {
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->returnurl = $returnurl;
+$toform->movecontext = $movecontext;
if ($cm !== null){
$toform->cmid = $cm->id;
+ $toform->courseid = $cm->course;
+} else {
+ $toform->courseid = $COURSE->id;
}
$mform->set_data($toform);
if ($mform->is_cancelled()){
redirect($returnurl);
} 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)) {
$question->id = 0; // causes a new question to be created.
$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);
- if ($QTYPES[$qtype]->finished_edit_wizard($data)){
+ if ($QTYPES[$qtype]->finished_edit_wizard($data) || $movecontext){
+
if (optional_param('inpopup', 0, PARAM_BOOL)) {
notify(get_string('changessaved'), '');
close_window(3);
} else {
redirect($returnurl);
}
- die;
} else {
- //useful for passing data to the next page which is not saved in the database
- $queryappend = '';
- if (isset($data->nextpageparam)){
- foreach ($data->nextpageparam as $key => $param){
- $queryappend .= "&".urlencode($key).'='.urlencode($param);
- }
- }
+ $nexturlparams = array('returnurl'=>$returnurl)
+ + $data->nextpageparam;//useful for passing data to the next page which is not saved in the database
if ($question->id) {
- $nexturl = "question.php?id=$question->id&returnurl=" . urlencode($returnurl);
+ $nexturlparams['id'] = $question->id;
} 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 {
list($streditingquestion,) = $QTYPES[$question->qtype]->get_heading();
if ($cm !== null) {
- $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $category->course))
- ? update_module_button($cm->id, $category->course, get_string('modulename', $cm->modname))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $COURSE->id))
+ ? update_module_button($cm->id, $cm->course, get_string('modulename', $cm->modname))
: "";
$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' => $streditingquestion, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
print_header_simple($streditingquestion, '', $navigation, "", "", true, $strupdatemodule);
-
+
} else {
$navlinks = array();
$navlinks[] = array('name' => get_string('editquestions', "quiz"), 'link' => $returnurl, 'type' => 'title');
print_header_simple($streditingquestion, '', $navigation);
}
+
// Display a heading, question editing form and possibly some extra content needed for
// for this question type.
$QTYPES[$question->qtype]->display_question_editing_page($mform, $question, $wizardnow);
-
print_footer($COURSE);
}
?>
include_once($CFG->libdir.'/questionlib.php');
- function restore_question_categories($category,$restore) {
-
- global $CFG;
+ /**
+ * Returns the best question category (id) found to restore one
+ * question category from a backup file. Works by stamp.
+ *
+ * @param object $restore preferences for restoration
+ * @param array $contextinfo fragment of decoded xml
+ * @return object best context instance for this category to be in
+ */
+ function restore_question_get_best_category_context($restore, $contextinfo) {
+ 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;
-
- //Hook to call Moodle < 1.5 Quiz Restore
- if ($restore->backup_version < 2005043000) {
- include_once($CFG->dirroot.'/mod/quiz/restorelibpre15.php');
- return quiz_restore_pre15_question_categories($category,$restore);
+ //Iterate over each category
+ foreach ($info as $category) {
+ $status = $status && restore_question_category($category, $restore);
}
+ $status = $status && restore_recode_category_parents($restore);
+ return $status;
+ }
- //Get record from backup_ids
- $data = backup_getid($restore->backup_unique_code,"question_categories",$category->id);
-
- if ($data) {
- //Now get completed xmlized object
- $info = $data->info;
- //traverse_xmlize($info); //Debug
- //print_object ($GLOBALS['traverse_array']); //Debug
- //$GLOBALS['traverse_array']=""; //Debug
+ 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
+ $data = backup_getid($restore->backup_unique_code, "question_categories", $category->id);
+
+ if ($data) {
+ //Now get completed xmlized object
+ $info = $data->info;
+ //traverse_xmlize($info); //Debug
+ //print_object ($GLOBALS['traverse_array']); //Debug
+ //$GLOBALS['traverse_array']=""; //Debug
- //Now, build the question_categories record structure
- $question_cat = new stdClass;
- $question_cat->course = $restore->course_id;
- $question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['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->parent = backup_todb($info['QUESTION_CATEGORY']['#']['PARENT']['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 {
+ //Now, build the question_categories record structure
+ $question_cat = new stdClass;
+ $question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['0']['#']);
+ $question_cat->info = backup_todb($info['QUESTION_CATEGORY']['#']['INFO']['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->sortorder = backup_todb($info['QUESTION_CATEGORY']['#']['SORTORDER']['0']['#']);
if (!$question_cat->stamp) {
$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
- if ($newid) {
+ //Do some output
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string('category', 'quiz')." \"".$question_cat->name."\"<br />";
}
- } else {
- //We must never arrive here !!
+
+ backup_flush(300);
+
+ //start with questions
+ if ($question_cat->id) {
+ //We have the newid, update backup_ids
+ //Now restore question
+ $status = restore_questions($category->id, $question_cat, $info, $restore);
+ } else {
+ $status = false;
+ }
if (!defined('RESTORE_SILENTLY')) {
- echo "<li>".get_string('category', 'quiz')." \"".$question_cat->name."\" Error!<br />";
+ echo '</li>';
}
- $status = false;
+ } else {
+ echo 'Could not get backup info for question category'. $category->id;
}
- backup_flush(300);
+ }
+ return $status;
+ }
- //Here category has been created or selected, so save results in backup_ids and start with questions
- if ($newid and $status) {
- //We have the newid, update backup_ids
- backup_putid($restore->backup_unique_code,"question_categories",
- $category->id, $newid);
- //Now restore question
- $status = restore_questions ($category->id, $newid,$info,$restore);
- } else {
- $status = false;
+ 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);
+ }
}
- if (!defined('RESTORE_SILENTLY')) {
- echo '</li>';
+ //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);
}
- } else {
- echo 'Could not get backup info for question category'. $category->id;
}
-
return $status;
}
- function restore_questions ($old_category_id,$new_category_id,$info,$restore) {
+ function restore_questions ($old_category_id, $best_question_cat, $info, $restore) {
global $CFG, $QTYPES;
//Now, build the question record structure
$question = new object;
- $question->category = $new_category_id;
$question->parent = backup_todb($que_info['#']['PARENT']['0']['#']);
$question->name = backup_todb($que_info['#']['NAME']['0']['#']);
$question->questiontext = backup_todb($que_info['#']['QUESTIONTEXT']['0']['#']);
$question->stamp = backup_todb($que_info['#']['STAMP']['0']['#']);
$question->version = backup_todb($que_info['#']['VERSION']['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) {
// The qtype was an integer that now needs to be converted to the name
$question->qtype = $qtypenames[$question->qtype];
}
- //Check if the question exists
- //by category, stamp, and version
- $question_exists = get_record ("question","category",$question->category,
- "stamp",$question->stamp,"version",$question->version);
-
+ //Check if the question exists by category, stamp, and version
+ //first check for the question in the context specified in backup
+ $existingquestion = get_record ("question", "category", $best_question_cat->id, "stamp", $question->stamp,"version",$question->version);
//If the question exists, only record its id
- if ($question_exists) {
- $newid = $question_exists->id;
+ //always use existing question, no permissions check here
+ if ($existingquestion) {
+ $question = $existingquestion;
$creatingnewquestion = false;
- //Else, create a new question
} else {
- //The structure is equal to the db, so insert the question
- $newid = insert_record ("question",$question);
- $creatingnewquestion = true;
+ //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
+ $question->id = insert_record ("question", $question);
+ $creatingnewquestion = true;
+ } else {
+ $question = $existingquestion;
+ $creatingnewquestion = false;
+ }
}
//Save newid to backup tables
- if ($newid) {
+ if ($question->id) {
//We have the newid, update backup_ids
- backup_putid($restore->backup_unique_code,"question",$oldid,
- $newid);
+ backup_putid($restore->backup_unique_code, "question", $oldid, $question->id);
}
$restored_questions[$i] = new stdClass;
- $restored_questions[$i]->newid = $newid;
+ $restored_questions[$i]->newid = $question->id;
$restored_questions[$i]->oldid = $oldid;
$restored_questions[$i]->qtype = $question->qtype;
- $restored_questions[$i]->parent = $question->parent;
+ $restored_questions[$i]->parent = $question->parent;
$restored_questions[$i]->is_new = $creatingnewquestion;
}
+ backup_flush(300);
// Loop again, now all the question id mappings exist, so everything can
// be restored.
echo 'Could not recode parent '.$question->parent.' for question '.$oldid.'<br />';
}
}
-
+
//Now, restore every question_answers in this question
$status = question_restore_answers($oldid,$newid,$que_info,$restore);
// Restore questiontype specific data
$status = true;
$qtype = backup_todb($info['#']['QTYPE']['0']['#']);
-
+
//Get the answers array
if (isset($info['#']['ANSWERS']['0']['#']['ANSWER'])) {
$answers = $info['#']['ANSWERS']['0']['#']['ANSWER'];
//print_object ($GLOBALS['traverse_array']); //Debug
//$GLOBALS['traverse_array']=""; //Debug
- // Check to see if this until already exists in the database, which it might, for
+ // Check to see if this until already exists in the database, which it might, for
// Historical reasons.
$unit = backup_todb($nu_info['#']['UNIT']['0']['#']);
if (!record_exists('question_numerical_units', 'question', $new_question_id, 'unit', $unit)) {
-
+
//Now, build the question_numerical_UNITS record structure.
$numerical_unit = new stdClass;
$numerical_unit->question = $new_question_id;
$numerical_unit->multiplier = backup_todb($nu_info['#']['MULTIPLIER']['0']['#']);
$numerical_unit->unit = $unit;
-
+
//The structure is equal to the db, so insert the question_numerical_units
$newid = insert_record("question_numerical_units", $numerical_unit);
-
+
if (!$newid) {
$status = false;
}
if ($res_info['#']['MANUALCOMMENT']['0']['#']) {
$session->manualcomment = backup_todb($res_info['#']['MANUALCOMMENT']['0']['#']);
} else { // pre 1.7 backups
- $session->manualcomment = backup_todb($res_info['#']['COMMENT']['0']['#']);
+ $session->manualcomment = backup_todb($res_info['#']['COMMENT']['0']['#']);
}
-
+
//We have to recode the question field
$question = backup_getid($restore->backup_unique_code,"question",$session->questionid);
if ($question) {
}
$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.
- if ($questions = get_records_sql('SELECT q.id, q.qtype, q.questiontext, q.generalfeedback
- FROM ' . $CFG->prefix . 'question q,
- ' . $CFG->prefix . 'question_categories qc
- WHERE q.category = qc.id
- AND qc.course = ' . $restore->course_id)) {
+ if ($questions = get_records_sql('SELECT q.id, q.qtype, q.questiontext, q.generalfeedback '.
+ 'FROM ' . $CFG->prefix . 'question q, '.
+ $CFG->prefix . 'question_categories qc '.
+ 'WHERE q.category = qc.id '.
+ 'AND qc.contextid IN (' .$coursemodulecontextslist.')')) {
foreach ($questions as $question) {
$questiontext = restore_decode_content_links_worker($question->questiontext, $restore);
' . $CFG->prefix . 'question q,
' . $CFG->prefix . 'question_categories qc
WHERE qa.question = q.id
- AND q.category = qc.id
- AND qc.course = ' . $restore->course_id)) {
+ AND q.category = qc.id '.
+ 'AND qc.contextid IN ('.$coursemodulecontextslist.')')) {
foreach ($answers as $answer) {
$feedback = restore_decode_content_links_worker($answer->feedback, $restore);
if (!isset($currenttab)) {
$currenttab = '';
}
- if (!isset($course)) {
+ if (!isset($COURSE)) {
error('No course specified');
}
$tabs = array();
$inactive = array();
$row = array();
- questionbank_navigation_tabs($row, $context, 'courseid='.$course->id);
+ questionbank_navigation_tabs($row, $contexts, 'courseid='.$COURSE->id);
$tabs[] = $row;
print_tabs($tabs, $currenttab, array());
+
?>
$addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
-
+
$mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerhdr', 'qtype_calculated', '{no}'));
$repeated[] =& $mform->createElement('text', 'answers', get_string('correctanswerformula', 'quiz').'=', array('size' => 50));
- $repeatedoptions['answers']['type'] = PARAM_NOTAGS;
+ $repeatedoptions['answers']['type'] = PARAM_NOTAGS;
$creategrades = get_grade_options();
$gradeoptions = $creategrades->gradeoptions;
$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('htmleditor', 'feedback', get_string('feedback', 'quiz'));
+ $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
+ array('course' => $this->coursefilesid));
$repeatedoptions['feedback']['type'] = PARAM_RAW;
if (isset($this->question->options)){
} else {
$count = 0;
}
- $repeatsatstart = $count + 1;
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = $count + 1;
+ } else {
+ $repeatsatstart = $count;
+ }
$this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 1, get_string('addmoreanswerblanks', 'qtype_calculated'));
- $repeated = array();
+ $repeated = array();
$repeated[] =& $mform->createElement('header', 'unithdr', get_string('unithdr', 'qtype_numerical', '{no}'));
$repeated[] =& $mform->createElement('text', 'unit', get_string('unit', 'quiz'));
} else {
$countunits = 0;
}
- $repeatsatstart = $countunits + 1;
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = $countunits + 1;
+ } else {
+ $repeatsatstart = $countunits;
+ }
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_calculated', '{no}'));
$firstunit =& $mform->getElement('multiplier[0]');
}
$units = array_values($question->options->units);
// make sure the default unit is at index 0
- usort($units, create_function('$a, $b',
+ usort($units, create_function('$a, $b',
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
if (count($units)) {
}
$default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
$default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
- /* set the wild cards category display given that on loading the category element is
- unselected when processing this function but have a valid value when processing the
+ /* set the wild cards category display given that on loading the category element is
+ unselected when processing this function but have a valid value when processing the
update category button. The value can be obtain by
$qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
but is coded using existing functions
- */
+ */
$qu = new stdClass;
$el = new stdClass;
- /* no need to call elementExists() here */
- $el=$this->_form->getElement('category');
+ /* no need to call elementExists() here */
+ $el=$this->_form->getElement('category');
if($value =$el->getSelected()) {
$qu->category =$value[0];
}else {
$qu->category=$question->category;// on load $question->category is set by question.php
- }
+ }
$html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
$this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
$question = (object)((array)$question + $default_values);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
- // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
+ // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (ereg('\{=([^[:space:]}]*)}', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
}else {
$errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
- }
+ }
}
- }
+ }
$answers = $data['answers'];
$answercount = 0;
$maxgrade = false;
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
- }
+ }
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){
$errors['answers['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
- }
- }
+ }
+ }
foreach ($answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
/**
* This method prepare the $datasets in a format similar to dadatesetdefinitions_form.php
- * so that they can be saved
+ * so that they can be saved
* using the function save_dataset_definitions($form)
- * when creating a new calculated question or
+ * when creating a new calculated question or
* whenediting an already existing calculated question
- * or by function save_as_new_dataset_definitions($form, $initialid)
+ * or by function save_as_new_dataset_definitions($form, $initialid)
* when saving as new an already existing calculated question
- *
+ *
* @param object $form
* @param int $questionfromid default = '0'
- */
+ */
function preparedatasets(&$form , $questionfromid='0'){
// the dataset names present in the edit_question_form and edit_calculated_form are retrieved
$possibledatasets = $this->find_dataset_names($form->questiontext);
$mandatorydatasets += $this->find_dataset_names($answer);
}
// if there are identical datasetdefs already saved in the original question.
- // either when editing a question or saving as new
+ // either when editing a question or saving as new
// they are retrieved using $questionfromid
if ($questionfromid!='0'){
$form->id = $questionfromid ;
// they will defined and stored with datasetdefinitions_form.php
// the $options are not used here
if ($questionfromid!='0'){
-
+
foreach ($possibledatasets as $datasetname) {
if (!isset($datasets[$datasetname])) {
list($options, $selected) =
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
- * at the first step $wizardnow = 'question'
+ * at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
- * then the datasets are prepared and saved
- * at the second step $wizardnow = 'datasetdefinitions'
+ * then the datasets are prepared and saved
+ * at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
- * at the third step $wizardnow = 'datasetitems'
+ * at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
function save_question($question, $form, $course) {
$wizardnow = optional_param('wizardnow', '', PARAM_ALPHA);
$id = optional_param('id', 0, PARAM_INT); // question id
- // in case 'question'
- // for a new question $form->id is empty
- // when saving as new question
- // $question->id = 0, $form is $data from question2.php
- // and $data->makecopy is defined as $data->id is the initial question id
+ // in case 'question'
+ // for a new question $form->id is empty
+ // when saving as new question
+ // $question->id = 0, $form is $data from question2.php
+ // and $data->makecopy is defined as $data->id is the initial question id
// edit case. If it is a new question we don't necessarily need to
// return a valid question object
$this->preparedatasets($form);
$form->id = $question->id;
$this->save_dataset_definitions($form);
- } else if (!empty($form->makecopy)){
+ } else if (!empty($form->makecopy)){
$questionfromid = $form->id ;
$question = parent::save_question($question, $form, $course);
//prepare the datasets
- $this->preparedatasets($form,$questionfromid);
+ $this->preparedatasets($form,$questionfromid);
$form->id = $question->id;
$this->save_as_new_dataset_definitions($form,$questionfromid );
- } else {// editing a question
+ } else {// editing a question
$question = parent::save_question($question, $form, $course);
//prepare the datasets
$this->preparedatasets($form,$question->id);
- $form->id = $question->id;
+ $form->id = $question->id;
$this->save_dataset_definitions($form);
}
break;
case 'datasetdefinitions':
- $this->save_dataset_definitions($form);
+
+ $this->save_dataset_definitions($form);
break;
case 'datasetitems':
$this->save_dataset_items($question, $form);
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
- // This dataset is not used any more, delete it
+ // This dataset is not used any more, delete it
delete_records('question_datasets',
'question', $form->id,
'datasetdefinition', $datasetdef->id);
}
/** This function create a copy of the datasets ( definition and dataitems)
* from the preceding question if they remain in the new question
- * otherwise its create the datasets that have been added as in the
+ * otherwise its create the datasets that have been added as in the
* save_dataset_definitions()
*/
function save_as_new_dataset_definitions($form, $initialid) {
global $CFG ;
- // Get the datasets from the intial question
+ // Get the datasets from the intial question
$datasetdefinitions = $this->get_dataset_definitions($initialid, $form->dataset);
// $tmpdatasets contains those of the new question
$tmpdatasets = array_flip($form->dataset);
'question_dataset_definitions', $datasetdef)) {
error("Unable to create dataset $defid");
}
- //copy the dataitems
+ //copy the dataitems
$olditems = get_records_sql( // Use number as key!!
" SELECT itemnumber, value
FROM {$CFG->prefix}question_dataset_items
if (!insert_record('question_dataset_items', $item)) {
error("Unable to insert dataset item $item->itemnumber with $item->value for $datasetdef->name");
}
- $itemcount++;
+ $itemcount++;
}
//update item count
$datasetdef->itemcount =$itemcount;
- update_record('question_dataset_definitions', $datasetdef);
- } // end of copy the dataitems
+ update_record('question_dataset_definitions', $datasetdef);
+ } // end of copy the dataitems
}// end of copy the datasetdef
- // Create relation to the new question with this
- // copy as new datasetdef from the initial question
+ // Create relation to the new question with this
+ // copy as new datasetdef from the initial question
$questiondataset = new stdClass;
$questiondataset->question = $form->id;
$questiondataset->datasetdefinition = $datasetdef->id;
$mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_INT);
$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->addElement('hidden', 'wizard', 'datasetitems');
$mform->setType('wizard', PARAM_ALPHA);
$mform->setType('qtype', PARAM_ALPHA);
$mform->addElement('hidden', 'category');
$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->setType('id', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetitems');
function name() {
return 'description';
}
-
+
function is_usable_by_random() {
return false;
}
// For editing teachers print a link to an editing popup window
$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');
$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);
* all question types need. Question types should define their own
* class that inherits from this one, and implements the definition_inner()
* method.
- *
+ *
* @package questionbank
* @subpackage questiontypes
*/
*/
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;
- 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);
+
}
/**
// Standard fields at the start of the form.
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
- $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'), null,
- array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true));
+ if (!isset($this->question->id)){
+ //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')));
+
+ } else {
+ //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->addElement('text', 'name', get_string('questionname', 'quiz'), array('size' => 50));
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
$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->setHelpButton('questiontext', array(array('questiontext', get_string('questiontext', 'quiz'), 'quiz'), 'richtext'), false, 'editorhelpbutton');
$mform->addElement('format', 'questiontextformat', get_string('format'));
- make_upload_directory("$COURSE->id"); // Just in case
- $coursefiles = get_directory_list("$CFG->dataroot/$COURSE->id", $CFG->moddata);
+ make_upload_directory($this->coursefilesid); // Just in case
+ $coursefiles = get_directory_list("$CFG->dataroot/$this->coursefilesid", $CFG->moddata);
foreach ($coursefiles as $filename) {
if (mimeinfo("icon", $filename) == "image.gif") {
$images["$filename"] = $filename;
$mform->setDefault('penalty', 0.1);
$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->setHelpButton('generalfeedback', array('generalfeedback', get_string('generalfeedback', 'quiz'), 'quiz'));
// Any questiontype specific fields.
$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.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL);
+ $mform->addElement('hidden', 'movecontext');
+ $mform->setType('movecontext', 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', '');
+ $mform->setDefault('returnurl', 0);
$buttonarray = array();
- $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
- if (!empty($this->question->id)) {
- $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
+ 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'));
+ }
+ if ($this->question->formoptions->cansaveasnew){
+ $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
+ }
+ $buttonarray[] = &$mform->createElement('cancel');
+ } else {
+ // adding new question
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
+ $buttonarray[] = &$mform->createElement('cancel');
}
- $buttonarray[] = &$mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$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'));
+ }
}
/**
function qtype() {
return '';
}
+
}
?>
\ No newline at end of file
* @param MoodleQuickForm $mform the form being built.
*/
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->addElement('hidden', 'fraction', 0);
} else {
$countsubquestions = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countsubquestions;
+ }
$mform->setType('subanswer', PARAM_TEXT);
$mform->setType('subquestion', PARAM_TEXT);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['subanswers'];
$questions = $data['subquestions'];
$questioncount = 0;
/**
* Decode links in question type specific tables.
* @return bool success or failure.
- */
+ */
function decode_content_links_caller($questionids, $restore, &$i) {
$status = true;
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 ////
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'));
$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)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_NOTAGS);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
function validation($data){
//TODO would be nice to parse the question text here and output some error
//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 />
$questiontext= trim(strip_tags($data['questiontext']));
if ($questiontext==''){
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) {
$wrapped->qtype = 'multichoice';
$wrapped->single = 1;
+ $wrapped->answernumbering = 0;
$wrapped->correctfeedback = '';
$wrapped->partiallycorrectfeedback = '';
$wrapped->incorrectfeedback = '';
*/
function definition_inner(&$mform) {
global $QTYPES;
-
+
$menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
$mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
$mform->setDefault('single', 1);
$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('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)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_RAW);
$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->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->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);
}
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
function response_summary($question, $state, $length = 80) {
return implode(',', $this->get_actual_response($question, $state));
}
-
+
/// BACKUP FUNCTIONS ////////////////////////////
/*
/**
* Decode links in question type specific tables.
* @return bool success or failure.
- */
+ */
function decode_content_links_caller($questionids, $restore, &$i) {
$status = true;
function get_numbering_styles() {
return array('abc', 'ABC', '123', 'none');
}
-
+
function number_html($qnum) {
return '<span class="anun">' . $qnum . '<span class="anumsep">.</span></span> ';
}
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.
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$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);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + 1))?
- QUESTION_NUMANS_START : ($countanswers + 1);
-
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (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'));
//------------------------------------------------------------------------------------------
} else {
$countunits = 0;
}
- $repeatsatstart = $countunits + 2;
+
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = $countunits + 2;
+ } else {
+ $repeatsatstart = $countunits;
+ }
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_numerical'));
$firstunit =& $mform->getElement('multiplier[0]');
parent::set_data($question);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
// Check the answers.
$answercount = 0;
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
- *//** */
+ */
require_once($CFG->libdir . '/questionlib.php');
* experiences of the first few question type implementors, and improve the
* interface to meet their needs, rather the freeze the API prematurely and
* condem everyone to working round a clunky interface for ever afterwards.
- *
+ *
* @package questionbank
* @subpackage questiontypes
*/
}
/**
- * @return whether the question_answers.answer field needs to have
+ * @return whether the question_answers.answer field needs to have
* restore_decode_content_links_worker called on it.
*/
function has_html_answers() {
* @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.
*/
- function create_editing_form($submiturl, $question) {
+ function create_editing_form($submiturl, $question, $category, $contexts, $formeditable) {
global $CFG;
require_once("{$CFG->dirroot}/question/type/edit_question_form.php");
$definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php';
if (!class_exists($classname)) {
return null;
}
- return new $classname($submiturl, $question);
+ return new $classname($submiturl, $question, $category, $contexts, $formeditable);
}
/**
* @param string $wizardnow is '' for first page.
*/
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);
+ $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();
}
-
+
/**
* Method called by display_question_editing_page and by question.php to get heading for breadcrumbs.
- *
+ *
* @return array a string heading and the langmodule in which it was found.
*/
- function get_heading(){
+ function get_heading($adding = false){
$name = $this->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] == '[') {
// Legacy behavior, if the string was not in the proper qtype_name
// language file, look it up in the quiz one.
$langmodule = 'quiz';
- $strheading = get_string('editing' . $name, $langmodule);
+ $strheading = get_string($strtoget, $langmodule);
}
return array($strheading, $langmodule);
}
* is itself an object, shown next to the form fields.
*/
function save_question($question, $form, $course) {
+ global $USER;
// This default implementation is suitable for most
// question types.
// 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->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
}
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
$question->stamp = get_field('question', 'stamp', 'id', $question->id);
- if (!update_record("question", $question)) {
- error("Could not update question!");
+ $question->modifiedby = $USER->id;
+ $question->timemodified = time();
+ if (!update_record('question', $question)) {
+ error('Could not update question!');
}
} else { // Question is a new one
// Set the unique code
+ list($question->category,$contextid) = explode(',', $form->category);
$question->stamp = make_unique_id_code();
- if (!$question->id = insert_record("question", $question)) {
- error("Could not insert new question!");
+ $question->createdby = $USER->id;
+ $question->timecreated = time();
+ if (!$question->id = insert_record('question', $question)) {
+ error('Could not insert new question!');
}
}
if (is_array($extra_question_fields)) {
$question_extension_table = array_shift($extra_question_fields);
-
+
$function = 'update_record';
$options = get_record($question_extension_table, 'questionid', $question->id);
if (!$options) {
}
$options->$field = $question->$field;
}
-
+
if (!$function($question_extension_table, $options)) {
$result = new stdClass;
$result->error = 'Could not save question options for ' .
$extra_answer_fields = $this->extra_answer_fields();
// TODO save the answers, with any extra data.
-
+
return null;
}
* then this method will return an array of <link ...> tags that reference
* those stylesheets. This function will also call require_js()
* from ajaxlib.php, to get any necessary JavaScript linked in too.
- *
+ *
* The two parameters match the first two parameters of print_question.
- *
+ *
* @param object $question The question object.
* @param object $state The state object.
- *
- * @return an array of bits of HTML to add to the head of pages where
+ *
+ * @return an array of bits of HTML to add to the head of pages where
* this question is print_question-ed in the body. The array should use
* integer array keys, which have no significance.
*/
// Core question types should not use this mechanism. Their styles
// 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.
if ($this->already_done) {
return array();
}
$this->already_done = true;
-
+
$plugindir = $this->plugin_dir();
$baseurl = $this->plugin_baseurl();
$stylesheets = array();
}
return $contributions;
}
-
+
/**
* Prints the question including the number, grading details, content,
* feedback and interactions
// For editing teachers print a link to an editing popup window
$editlink = '';
- if ($context && has_capability('moodle/question:manage', $context)) {
+ if (question_has_capability_on($question, 'edit')) {
$stredit = get_string('edit');
$linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />';
$editlink = link_to_popup_window('/question/question.php?inpopup=1&id='.$question->id, 'editquestion', $linktext, 450, 550, $stredit, '', true);
if (($cmoptions->optionflags & QUESTION_ADAPTIVE) and !$options->readonly) {
echo '<input type="submit" name="', $question->name_prefix, 'submit" value="',
- get_string('mark', 'quiz'), '" class="submit btn" onclick="',
+ get_string('mark', 'quiz'), '" class="submit btn" onclick="',
"form.action = form.action + '#q", $question->id, "'; return true;", '" />';
}
}
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.
* @param $cmoptions as passed in from outside.
// Standard fields at the start of the form.
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
- $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
- array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true));
+ $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
+ array('contexts' => $this->contexts->having_cap('moodle/question:useall')));
$mform->addElement('text', 'name', get_string('questionname', 'quiz'),
array('size' => 50));
$mform->addElement('hidden', 'versioning');
$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[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
function validation($data){
global $QTYPES;
- $errors = array();
+ $errors = parent::validation($data);
$saquestions = $QTYPES['randomsamatch']->get_sa_candidates($data['category']);
$numberavailable = count($saquestions);
if ($saquestions === false){
$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('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)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$mform->setType('answer', PARAM_RAW);
$repeatedoptions['fraction']['default'] = 0;
parent::set_data($question);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
$mform->addElement('select', 'correctanswer', get_string('correctanswer', '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->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);
// Fix penalty factor at 1.
* @author T.J.Hunt@open.ac.uk
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package package_name
- *//** */
+ */
/**
* This test is becuase the RQP question type was included in core
* up to and including Moodle 1.8, and was removed before Moodle 1.9.
- *
+ *
* Therefore, we want to check whether any rqp questions exist in the database
- * before doing the upgrade. However, the check is not relevant if that
- * question type was never installed, or if the person has chosen to
+ * before doing the upgrade. However, the check is not relevant if that
+ * question type was never installed, or if the person has chosen to
* 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.
*/
function question_check_no_rqp_questions($result) {
global $CFG;
$result = true;
-
+
// Only remove the question type if the code is gone.
if (!is_dir($CFG->dirroot . '/question/type/rqp')) {
$table = new XMLDBTable('question_rqp_states');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp_types');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp_servers');
$result = $result && drop_table($table);
-
+
$result = $result && unset_config('qtype_rqp_version');
}
-
+
return $result;
}
if (!empty($CFG->qtype_rqp_version) && !is_dir($CFG->dirroot . '/question/type/rqp')) {
$result = $result && unset_config('qtype_rqp_version');
}
-
+
+ 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;
}
?>
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
- $version = 2007080903; // YYYYMMDD = date
+ $version = 2007081000; // YYYYMMDD = date
// XY = increments within a single day
$release = '1.9 dev'; // Human-friendly version name