merging MOODLE_19_QUESTIONS with HEAD

This commit is contained in:
jamiesensei 2007-08-09 21:50:59 +00:00
parent e8a46dcc2c
commit 3bee1ead40
19 changed files with 1072 additions and 596 deletions

View file

@ -175,6 +175,11 @@
<ON_OK message="qtyperqpwillberemoved" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/upgrade.php" function="question_random_check" level="optional">
<FEEDBACK>
<ON_OK message="questioncwqpfsok" />
</FEEDBACK>
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="2.0">

View file

@ -26,6 +26,7 @@ $temp->add(new admin_setting_backupselect('backup_sche_users', get_string('users
$temp->add(new admin_setting_backupcheckbox('backup_sche_logs', get_string('logs'), get_string('backuplogshelp'), 0));
$temp->add(new admin_setting_backupcheckbox('backup_sche_userfiles', get_string('userfiles'), get_string('backupuserfileshelp'), 0));
$temp->add(new admin_setting_backupcheckbox('backup_sche_coursefiles', get_string('coursefiles'), get_string('backupcoursefileshelp'), 0));
$temp->add(new admin_setting_backupcheckbox('backup_sche_sitefiles', get_string('sitefiles'), get_string('backupsitefileshelp'), 0));
$temp->add(new admin_setting_backupcheckbox('backup_sche_messages', get_string('messages', 'message'), get_string('backupmessageshelp','message'), 0));
$temp->add(new admin_setting_backupselect('backup_sche_keep', get_string('keep'), get_string('backupkeephelp'), 1, array(0 => get_string('all'),
1 => '1',

View file

@ -129,5 +129,4 @@
//Print footer
print_footer();
?>

View file

@ -1,4 +1,5 @@
<?php //$Id$
require_once("$CFG->dirroot/question/backuplib.php");
//This page prints the backup todo list to see everything
//Check login
@ -26,6 +27,8 @@
$count = 0;
backup_fetch_prefs_from_request($backupprefs,$count,$course);
//Check site
if (!$site = get_site()) {
error("Site not found!");
@ -33,6 +36,11 @@
if ($count == 0) {
notice("No backupable modules are installed!");
}
if (!execute_sql("DELETE FROM {$CFG->prefix}backup_ids WHERE backup_code = '{$backupprefs->backup_unique_code}'",false)){
error('Couldn\'t delete previous backup ids.');
}
?>
<form id="form" method="post" action="backup.php">
@ -90,7 +98,7 @@
echo "</b></td></tr>";
}
//This is tha align to every ingo table
//This is the alignment of every row in the table
$table->align = array ("left","right");
if ($allmods = get_records("modules") ) {
@ -111,9 +119,6 @@
//Print the full tr
echo "<tr>";
echo "<td colspan=\"2\">";
//Add hidden fields
$var = "backup_".$modname;
$var = "backup_user_info_".$modname;
//Print the mod name
echo "<b>".get_string("include")." ".get_string("modulenameplural",$modname)." ";
//Now look for user-data status
@ -201,6 +206,16 @@
print_table($table);
echo "</td></tr>";
}
//Now print the site Files tr conditionally
if ($backupprefs->backup_site_files) {
echo "<tr>";
echo "<td colspan=\"2\"><b>";
echo get_string("includesitefiles").'</b>';
//Print info
$table->data = site_files_check_backup($id,$backupprefs->backup_unique_code);
print_table($table);
echo "</td></tr>";
}
}
// now keep it for next time.

View file

@ -64,6 +64,7 @@
$backup_logs = optional_param('backup_logs',0);
$backup_user_files = optional_param('backup_user_files',1);
$backup_course_files = optional_param('backup_course_files',1);
$backup_site_files = optional_param('backup_site_files',1);
$backup_gradebook_history = optional_param('backup_gradebook_history', 1, PARAM_INT);
$backup_messages = optional_param('backup_messages',1);
@ -261,6 +262,19 @@ function selectItemInCheckboxByName(formId, checkName, checked ) {
choose_from_menu($course_file_options, "backup_course_files", $backup_course_files, "");
echo "</td></tr>";
if ($course->id != SITEID){
//Now print the site Files tr
echo "<tr>";
echo "<td align=\"right\" colspan=\"2\"><b>";
echo get_string ("sitefilesused").":";
echo "</b></td><td colspan=\"2\">";
$course_file_options[0] = get_string("no");
$course_file_options[1] = get_string("yes");
choose_from_menu($course_file_options, "backup_site_files", $backup_site_files, "");
echo "</td></tr>";
}
// do you want grade histories to be backed up?
echo "<tr>";
echo "<td align=\"right\" colspan=\"2\"><b>";

View file

@ -334,6 +334,9 @@ function schedule_backup_course_configure($course,$starttime = 0) {
if (!isset($backup_config->backup_sche_coursefiles)) {
$backup_config->backup_sche_coursefiles = 1;
}
if (!isset($backup_config->backup_sche_sitefiles)) {
$backup_config->backup_sche_sitefiles = 1;
}
if (!isset($backup_config->backup_sche_messages)) {
$backup_config->backup_sche_messages = 0;
}
@ -424,6 +427,7 @@ function schedule_backup_course_configure($course,$starttime = 0) {
$preferences->backup_logs = $backup_config->backup_sche_logs;
$preferences->backup_user_files = $backup_config->backup_sche_userfiles;
$preferences->backup_course_files = $backup_config->backup_sche_coursefiles;
$preferences->backup_site_files = $backup_config->backup_sche_sitefiles;
$preferences->backup_messages = $backup_config->backup_sche_messages;
$preferences->backup_course = $course->id;
$preferences->backup_destination = $backup_config->backup_sche_destination;
@ -547,6 +551,14 @@ function schedule_backup_course_configure($course,$starttime = 0) {
}
}
//Now calculate the sitefiles
if ($status) {
if ($preferences->backup_site_files) {
schedule_backup_log($starttime,$course->id," calculating site files");
site_files_check_backup($course->id,$preferences->backup_unique_code);
}
}
//If everything is ok, return calculated preferences
if ($status) {
$status = $preferences;
@ -730,6 +742,14 @@ function schedule_backup_course_execute($preferences,$starttime = 0) {
}
}
//Now, if selected, copy site files
if ($status) {
if ($preferences->backup_site_files) {
schedule_backup_log($starttime,$preferences->backup_course," copying site files");
$status = backup_copy_site_files ($preferences);
}
}
//Now, zip all the backup directory contents
if ($status) {
schedule_backup_log($starttime,$preferences->backup_course," zipping files");

View file

@ -249,11 +249,13 @@
return $info;
}
//Calculate the number of course files to backup
//under $CFG->dataroot/$course, except $CFG->moddata, and backupdata
//and put them (their path) in backup_ids
//Return an array of info (name,value)
function course_files_check_backup($course,$backup_unique_code) {
/**
* Calculate the number of course files to backup
* under $CFG->dataroot/$course, except $CFG->moddata, and backupdata
* and put them (their path) in backup_ids
* Return an array of info (name,value)
*/
function course_files_check_backup($course, $backup_unique_code) {
global $CFG;
@ -283,6 +285,8 @@
WHERE backup_code = '$backup_unique_code' AND
file_type = 'course'");
//Gets the user data
$info = array();
$info[0] = array();
$info[0][0] = get_string("files");
if ($ids) {
$info[0][1] = count($ids);
@ -293,6 +297,35 @@
return $info;
}
/**
* Calculate the number of site files to backup
* under $CFG->dataroot/SITEID
* Their path is already in backup_ids, put there by modules check_backup functions.
* Modules only put in paths of files that are used.
*
* Return an array of info (name,value)
*/
function site_files_check_backup($course, $backup_unique_code) {
global $CFG;
//execute the select, records have been inserted by modules during their ****_check_backup_mods function.
$ids = get_records_sql("SELECT DISTINCT b.path
FROM {$CFG->prefix}backup_files b
WHERE backup_code = '$backup_unique_code' AND
file_type = 'site'");
//Gets the user data
$info = array();
$info[0] = array();
$info[0][0] = get_string('files');
if ($ids) {
$info[0][1] = count($ids);
} else {
$info[0][1] = 0;
}
return $info;
}
//Function to check and create the needed moddata dir to
//save all the mod backup files. We always name it moddata
//to be able to restore it, but in restore we check for
@ -301,7 +334,7 @@
global $CFG;
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/moddata",true);
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/moddata",true);
return $status;
}
@ -312,7 +345,7 @@
global $CFG;
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/user_files",true);
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/user_files",true);
return $status;
}
@ -323,7 +356,7 @@
global $CFG;
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/group_files",true);
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/group_files",true);
return $status;
}
@ -334,7 +367,18 @@
global $CFG;
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/course_files",true);
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/course_files",true);
return $status;
}
//Function to check and create the "site_files" dir to
//save all the course files we need from "CFG->datadir/SITEID" dir
function check_and_create_site_files_dir($backup_unique_code) {
global $CFG;
$status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code."/site_files",true);
return $status;
}
@ -540,6 +584,12 @@
} else {
fwrite ($bf,full_tag("COURSEFILES",3,false,"false"));
}
//The course files
if ($preferences->backup_site_files == 1) {
fwrite ($bf,full_tag("SITEFILES",3,false,"true"));
} else {
fwrite ($bf,full_tag("SITEFILES",3,false,"false"));
}
//The messages in backup
if ($preferences->backup_messages == 1 && $preferences->backup_course == SITEID) {
fwrite ($bf,full_tag("MESSAGES",3,false,"true"));
@ -1363,7 +1413,7 @@
global $CFG;
$status = true;
// getting grade categories, but make sure parents come before children
// getting grade categories, but make sure parents come before children
// because when we do restore, we need to recover the parents first
// we do this by getting the lowest depth first
$grade_categories = get_records_sql("SELECT * FROM {$CFG->prefix}grade_categories
@ -1745,12 +1795,12 @@
fwrite ($bf,end_tag("GRADE_ITEM_HISTORY",6,true));
}
$status = fwrite ($bf,end_tag("GRADE_ITEM_HISTORIES",5,true));
}
return $status;
}
function backup_gradebook_outcomes_history($bf, $preferences) {
global $CFG;
$status = true;
@ -2237,7 +2287,42 @@
}
return $status;
}
/*
* This function copies all the site files under the site directory (except the moddata and backupdata
* directories to the "site_files" directory under temp/backup
*/
function backup_copy_site_files ($preferences) {
global $CFG;
$status = true;
if ($preferences->backup_course == SITEID){
return $status;
}
//First we check to "site_files" exists and create it as necessary
//in temp/backup/$backup_code dir
$status = $status && check_and_create_site_files_dir($preferences->backup_unique_code);
$rootdir = $CFG->dataroot."/".SITEID;
$files = get_records_select('backup_files',
"backup_code = '$preferences->backup_unique_code' AND file_type = 'site'");
if ($files) {
//Iterate
foreach ($files as $fileobj) {
//check for dir structure and create recursively
$file = $fileobj->path;
$status = $status && check_dir_exists(dirname($CFG->dataroot."/temp/backup/".$preferences->backup_unique_code."/site_files/".$file), true, true);
$status = $status && backup_copy_file($rootdir."/".$file,
$CFG->dataroot."/temp/backup/".$preferences->backup_unique_code."/site_files/".$file);
}
}
return $status;
}
//This function creates the zip file containing all the backup info
//moodle.xml, moddata, user_files, course_files.
//The zipped file is created in the backup directory and named with
@ -2424,13 +2509,14 @@
$preferences->backup_user_files = optional_param('backup_user_files',1,PARAM_INT);
$preferences->backup_course_files = optional_param('backup_course_files',1,PARAM_INT);
$preferences->backup_gradebook_history = optional_param('backup_gradebook_history', 1, PARAM_INT);
$preferences->backup_site_files = optional_param('backup_site_files',1,PARAM_INT);
$preferences->backup_messages = optional_param('backup_messages',1,PARAM_INT);
$preferences->backup_course = $course->id;
$preferences->backup_name = required_param('backup_name',PARAM_FILE);
$preferences->backup_unique_code = required_param('backup_unique_code');
// put it (back) in the session
$SESSION->backupprefs[$course->id] = $preferences;
$SESSION->backupprefs[$course->id] = $preferences;
}
/* Finds all related roles used in course, mod and blocks context
@ -2727,14 +2813,14 @@
}
}
//If we have selected to backup quizzes, backup categories and
//questions structure (step 1). See notes on mod/quiz/backuplib.php
if ($status and !empty($preferences->mods['quiz']->backup)) {
//If we have selected to backup quizzes or other modules that use questions
//we've already added ids of categories and questions to backup to backup_ids table
if ($status) {
if (!defined('BACKUP_SILENTLY')) {
echo "<li>".get_string("writingcategoriesandquestions").'</li>';
}
require_once($CFG->dirroot.'/mod/quiz/backuplib.php');
if (!$status = backup_question_categories($backup_file,$preferences)) {
require_once($CFG->dirroot.'/question/backuplib.php');
if (!$status = backup_question_categories($backup_file, $preferences)) {
if (!defined('BACKUP_SILENTLY')) {
notify("An error occurred while backing up quiz categories");
}
@ -2980,7 +3066,23 @@
}
}
}
//Now, if selected, copy site files
if ($status) {
if ($preferences->backup_site_files) {
if (!defined('BACKUP_SILENTLY')) {
echo "<li>".get_string("copyingsitefiles").'</li>';
}
if (!$status = backup_copy_site_files ($preferences)) {
if (!defined('BACKUP_SILENTLY')) {
notify("An error occurred while copying site files");
}
else {
$errorstr = "An error occurred while copying site files";
return false;
}
}
}
}
//Now, zip all the backup directory contents
if ($status) {
if (!defined('BACKUP_SILENTLY')) {

View file

@ -758,6 +758,7 @@
backup_logs
backup_user_files
backup_course_files
backup_site_files
backup_messages
* and if not provided, they will not be included.
*/
@ -828,6 +829,7 @@
$preferences->backup_logs = (isset($prefs['backup_logs']) ? $prefs['backup_logs'] : 0);
$preferences->backup_user_files = (isset($prefs['backup_user_files']) ? $prefs['backup_user_files'] : 0);
$preferences->backup_course_files = (isset($prefs['backup_course_files']) ? $prefs['backup_course_files'] : 0);
$preferences->backup_site_files = (isset($prefs['backup_site_files']) ? $prefs['backup_site_files'] : 0);
$preferences->backup_messages = (isset($prefs['backup_messages']) ? $prefs['backup_messages'] : 0);
$preferences->backup_course = $course->id;
backup_add_static_preferences($preferences);

View file

@ -82,6 +82,8 @@
$restore_user_files = required_param('restore_user_files');
//restore_course_files
$restore_course_files = required_param('restore_course_files');
//restore_site_files
$restore_site_files = required_param('restore_site_files');
//restore_messages
$restore_messages = required_param('restore_messages');
@ -117,6 +119,7 @@
$restore->logs=$restore_logs;
$restore->user_files=$restore_user_files;
$restore->course_files=$restore_course_files;
$restore->site_files=$restore_site_files;
$restore->messages=$restore_messages;
$restore->course_id=$course_id;
//add new vars to restore object

View file

@ -87,6 +87,10 @@
$restore_course_files = 1;
}
if (!isset($restore_site_files)) {
$restore_site_files = 1;
}
if (!isset($restore_messages)) {
$restore_messages = 1;
}
@ -431,6 +435,23 @@ function selectItemInCheckboxByName(formId, checkName, checked ) {
}
echo "</td></tr>";
//Now print the Site Files tr
echo "<tr>";
echo "<td align=\"right\" colspan=\"2\"><b>";
echo get_string ("sitefiles").":";
echo "</b></td><td colspan=\"2\">";
//If site files are in the backup file, show menu, else fixed to no
if ($info->backup_site_files == "true") {
$site_file_options[0] = get_string("no");
$site_file_options[1] = get_string("yes");
choose_from_menu($site_file_options, "restore_site_files", $restore_site_files, "");
} else {
echo get_string("no");
echo "<input type=\"hidden\" name=\"restore_site_files\" value=\"0\" />";
}
echo "</td></tr>";
//Now print the Messages tr
echo "<tr>";
echo "<td align=\"right\" colspan=\"2\"><b>";

View file

@ -451,6 +451,14 @@
$tab[$elem][1] = get_string("no");
}
$elem++;
//site Files info
$tab[$elem][0] = "<b>".get_string("sitefiles").":</b>";
if ($info->backup_site_files == "true") {
$tab[$elem][1] = get_string("yes");
} else {
$tab[$elem][1] = get_string("no");
}
$elem++;
//Messages info (only showed if present)
if ($info->backup_messages == 'true') {
$tab[$elem][0] = "<b>".get_string('messages','message').":</b>";
@ -671,43 +679,7 @@
return $status;
}
/**
* Returns the best question category (id) found to restore one
* question category from a backup file. Works by stamp (since Moodle 1.1)
* or by name (for older versions).
*
* @param object $cat the question_categories record to be searched
* @param integer $courseid the course where we are restoring
* @return integer the id of a existing question_category or 0 (not found)
*/
function restore_get_best_question_category($cat, $courseid) {
$found = 0;
//Decide how to work (by stamp or name)
if ($cat->stamp) {
$searchfield = 'stamp';
$searchvalue = $cat->stamp;
} else {
$searchfield = 'name';
$searchvalue = $cat->name;
}
//First shot. Try to get the category from the course being restored
if ($fcat = get_record('question_categories','course',$courseid,$searchfield,$searchvalue)) {
$found = $fcat->id;
//Second shot. Try to obtain any concordant category and check its publish status and editing rights
} else if ($fcats = get_records('question_categories', $searchfield, $searchvalue, 'id', 'id, publish, course')) {
foreach ($fcats as $fcat) {
if ($fcat->publish == 1 && has_capability('moodle/site:restore', get_context_instance(CONTEXT_COURSE, $fcat->course))) {
$found = $fcat->id;
break;
}
}
}
return $found;
}
//This function creates all the block stuff when restoring courses
//It calls selectively to restore_create_block_instances() for 1.5
@ -1625,6 +1597,7 @@
}
/// processing grade_grades_text
if (!empty($info['GRADE_ITEM']['#']['GRADE_GRADES_TEXT']['0']['#']) && ($texts = $info['GRADE_ITEM']['#']['GRADE_GRADES_TEXT']['0']['#']['GRADE_TEXT'])) {
//Iterate over items
@ -2676,34 +2649,7 @@
//categories/questions
if ($info) {
if ($info !== true) {
//Iterate over each category
foreach ($info as $category) {
//Skip empty categories (some backups can contain them)
if (!empty($category->id)) {
$status = restore_question_categories($category,$restore);
}
}
//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) {
foreach ($categories as $category) {
$restoredcategory = get_record('question_categories','id',$category->new_id);
$restoredcategory = addslashes_object($restoredcategory);
if ($restoredcategory->parent != 0) {
$idcat = backup_getid($restore->backup_unique_code,'question_categories',$restoredcategory->parent);
if ($idcat->new_id) {
$restoredcategory->parent = $idcat->new_id;
} else {
$restoredcategory->parent = 0;
}
update_record('question_categories', $restoredcategory);
}
}
}
$status = $status && restore_question_categories($info, $restore);
}
} else {
$status = false;
@ -3413,6 +3359,59 @@
}
}
//This function restores the site files from the temp (site_files) directory to the
//dataroot/SITEID directory
function restore_site_files($restore) {
global $CFG;
$status = true;
$counter = 0;
//First, we check to "course_id" exists and create is as necessary
//in CFG->dataroot
$dest_dir = $CFG->dataroot."/".SITEID;
$status = check_dir_exists($dest_dir,true);
//Now, we iterate over "site_files" files to check if that file/dir must be
//copied to the "dest_dir" dir.
$rootdir = $CFG->dataroot."/temp/backup/".$restore->backup_unique_code."/site_files";
//Check if directory exists
if (is_dir($rootdir)) {
$list = list_directories_and_files ($rootdir);
if ($list) {
//Iterate
$counter = 0;
foreach ($list as $dir) {
//Copy the dir to its new location
//Only if destination file/dir doesn exists
if (!file_exists($dest_dir."/".$dir)) {
$status = backup_copy_file($rootdir."/".$dir,
$dest_dir."/".$dir,true);
$counter ++;
}
//Do some output
if ($counter % 2 == 0) {
if (!defined('RESTORE_SILENTLY')) {
echo ".";
if ($counter % 40 == 0) {
echo "<br />";
}
}
backup_flush(300);
}
}
}
}
//If status is ok and whe have dirs created, returns counter to inform
if ($status and $counter) {
return $counter;
} else {
return $status;
}
}
//This function creates all the structures for every module in backup file
//Depending what has been selected.
@ -4344,6 +4343,9 @@
case "COURSEFILES":
$this->info->backup_course_files = $this->getContents();
break;
case "SITEFILES":
$this->info->backup_site_files = $this->getContents();
break;
case "MESSAGES":
$this->info->backup_messages = $this->getContents();
break;
@ -6501,6 +6503,7 @@
$restore->backup_unique_code=$backup_unique_code;
$restore->users = 2; // yuk
$restore->course_files = $SESSION->restore->restore_course_files;
$restore->site_files = $SESSION->restore->restore_site_files;
if ($allmods = get_records("modules")) {
foreach ($allmods as $mod) {
$modname = $mod->name;
@ -6806,7 +6809,7 @@
//Now create categories and questions as needed
if ($status and ($restore->mods['quiz']->restore)) {
if ($status) {
include_once("$CFG->dirroot/question/restorelib.php");
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string("creatingcategoriesandquestions");
@ -6879,6 +6882,34 @@
}
}
//Now create site files as needed
if ($status and ($restore->site_files)) {
if (!defined('RESTORE_SILENTLY')) {
echo "<li>".get_string('copyingsitefiles');
}
if (!$status = restore_site_files($restore)) {
if (empty($status)) {
notify("Could not restore site files!");
} else {
$errorstr = "Could not restore site files!";
return false;
}
}
//If all is ok (and we have a counter)
if ($status and ($status !== true)) {
//Inform about user dirs created from backup
if (!defined('RESTORE_SILENTLY')) {
echo "<ul>";
echo "<li>".get_string("filesfolders").": ".$status.'</li>';
echo "</ul>";
}
}
if (!defined('RESTORE_SILENTLY')) {
echo "</li>";
}
}
//Now create messages as needed
if ($status and ($restore->messages)) {
if (!defined('RESTORE_SILENTLY')) {

View file

@ -502,7 +502,12 @@ $string['profilevisible'] = 'Who is this field visible to?';
$string['protectusernames'] = 'Protect usernames';
$string['proxyhost'] = 'Proxy host';
$string['proxyport'] = 'Proxy port';
$string['qtyperqpwillberemoved'] = 'During the upgrade, the RQP question type will be removed. You were not using this question type, so you should not experience any problems.';
$string['qtyperqpwillberemovedanyway'] = 'During the upgrade, the RQP question type will be removed. You have some RQP questions in your database, and these will stop working unless you reinstall the code from http://moodle.org/mod/data/view.php?d=13&amp;rid=797 before continuing with the upgrade.';
$string['quarantinedir'] = 'Quarantine directory';
$string['question'] = 'Question';
$string['questioncwqpfscheck'] = 'One or more \'random\' questions in a quiz are set up to select questions from a mixture of shared and unshared question categories. There is a more detailled report <a href=\"$a->reporturl\">here</a> and see Moodle Docs page <a href=\"$a->docsurl\">here</a>.';
$string['questioncwqpfsok'] = 'Good. There are no \'random\' questions in your quizzes that are set up to select questions from a mixture of shared and unshared question categories.';
$string['rcache'] = 'Record cache';
$string['rcachettl'] = 'Record cache TTL';
$string['releasenoteslink'] = 'For information about this version of Moodle, please see the online <a target=\"_new\" href=\"$a\">Release Notes</a>';
@ -521,8 +526,6 @@ $string['riskspamshort'] = 'Spam risk';
$string['riskxss'] = 'Users could add files and texts that allow cross-site scripting (XSS)';
$string['riskxssshort'] = 'XSS risk';
$string['runclamavonupload'] = 'Use clam AV on uploaded files';
$string['qtyperqpwillberemoved'] = 'During the upgrade, the RQP question type will be removed. You were not using this question type, so you should not experience any problems.';
$string['qtyperqpwillberemovedanyway'] = 'During the upgrade, the RQP question type will be removed. You have some RQP questions in your database, and these will stop working unless you reinstall the code from http://moodle.org/mod/data/view.php?d=13&amp;rid=797 before continuing with the upgrade.';
$string['savechanges'] = 'Save Changes';
$string['search'] = 'Search';
$string['searchresults'] = 'Search Results';

View file

@ -159,6 +159,7 @@ $string['backupnonisowarning'] = 'Warning: this backup is from a non-Unicode ver
$string['backuporiginalname'] = 'Backup Name';
$string['backupsavetohelp'] = 'Full path to the directory where you want to save the backup files<br />(leave blank to save in its course default dir)';
$string['backupschedulehelp'] = 'Choose which days of the week to perform automated backups.';
$string['backupsitefileshelp'] = 'If enabled then site files used in courses will be included in automated backups';
$string['backuptakealook'] = 'Please take a look at your backup logs in:
$a';
$string['backupuserfileshelp'] = 'Choose whether user files (eg profile images) should be included in automated backups';
@ -239,6 +240,7 @@ $string['cookiesnotenabled'] = 'Unfortunately, cookies are currently not enabled
$string['copy'] = 'copy';
$string['copyasnoun'] = 'copy';
$string['copyingcoursefiles'] = 'Copying course files';
$string['copyingsitefiles'] = 'Copying site files used in course';
$string['copyinguserfiles'] = 'Copying user files';
$string['copyingzipfile'] = 'Copying zip file';
$string['copyrightnotice'] = 'Copyright notice';
@ -775,6 +777,7 @@ $string['includemodules'] = 'Include Modules';
$string['includemoduleuserdata'] = 'Include module user data';
$string['includeneededusers'] = 'Include Needed Users';
$string['includenoneusers'] = 'Include No Users';
$string['includesitefiles'] = 'Include Site Files Used in This Course';
$string['includeuserfiles'] = 'Include User Files';
$string['info'] = 'Information';
$string['institution'] = 'Institution';
@ -1299,6 +1302,7 @@ $string['site'] = 'Site';
$string['sitedefault'] = 'Site Default';
$string['siteerrors'] = 'Site errors';
$string['sitefiles'] = 'Site files';
$string['sitefilesused'] = 'Site files used in this course';
$string['sitelogs'] = 'Site logs';
$string['sitenews'] = 'Site news';
$string['sitepartlist'] = 'You do not have the required permissions to view the participants list';

View file

@ -1,6 +1,7 @@
<?PHP // $Id$
// qtype_multichoice.php - created with Moodle 1.7 beta + (2006101003)
$string['addingmultichoice'] = 'Adding a Multiple Choice question';
$string['answerhowmany'] = 'One or multiple answers?';
$string['addmorechoiceblanks'] = 'Blanks for {no} More Choices';
$string['answerhowmany'] = 'One or multiple answers?';

View file

@ -1,6 +1,7 @@
<?php // $Id$
// qtype_truefalse.php - created with Moodle 1.8dev
$string['addingtruefalse'] = 'Adding a True/False question';
$string['correctanswer'] = 'Correct answer';
$string['editingtruefalse'] = 'Editing a True/False question';
$string['false'] = 'False';

View file

@ -1,14 +1,78 @@
<?php // $Id$
// question.php - created with Moodle 1.8 dev
$string['adminreport'] = 'Report on possible problems in your question database.';
$string['broken'] = 'This is a \"broken link\", it points to a nonexistent file.';
$string['byandon'] = 'by <em>$a->user</em> on <em>$a->time</em>';
$string['categorydoesnotexist'] = 'This category does not exist';
$string['categorycurrent'] = 'Current Category';
$string['categorycurrentuse'] = 'Use This Category';
$string['categorymoveto'] = 'Save in Category';
$string['changepublishstatuscat'] = '<a href=\"$a->caturl\">Category \"$a->name\"</a> in course \"$a->coursename\" will have it\'s sharing status changed from <strong>$a->changefrom to $a->changeto</strong>.';
$string['cwrqpfs'] = 'Random questions selecting questions from sub categories.';
$string['cwrqpfsinfo'] = '<p>During the upgrade to Moodle 1.9 we will seperate question categories into
different contexts. Some question categories and questions on your site will have to have their sharing
status changed. This is necessary in the rare case that one or more \'random\' questions in a quiz are set up to select from a mixture of
shared and unshared categories (as is the case on this site). This happens when a \'random\' question is set to select
from subcategories and one or more subcategories have a different sharing status to the parent category in which
the random question is created.</p>
<p>The following question categories, from which \'random\' questions in parent categories select questions from, will have their sharing status changed to the same sharing status as the category with the \'random\' question in on upgrading to Moodle 1.9. The following categories will have their sharing status changed. Questions which are affected will continue to work in all existing quizzes until you remove them from these quizzes.';
$string['cwrqpfsnoprob'] = 'No question categories in your site are affected by the \'Random questions selecting questions from sub categories\' issue.';
$string['copy']= 'Copy from $a and change links.';
$string['created'] = 'Created';
$string['createdmodifiedheader'] = 'Created / Modified';
$string['defaultfor'] = 'Default for $a';
$string['defaultinfofor'] = 'The default category for questions shared in context \'$a\'.';
$string['donothing']= 'Don\'t copy or move files or change links.';
$string['editingcategory'] = 'Editing a category';
$string['editingquestion'] = 'Editing a question';
$string['missingimportantcode'] = 'This question type is missing important code: $a.';
$string['notenoughdatatoeditaquestion'] = 'Neither a question id, nor a category id and question type, was specified.';
$string['questionbank'] = 'Question bank';
$string['questiondoesnotexist'] = 'This question does not exist';
$string['unknownquestiontype'] = 'Unknown question type: $a.';
$string['erroraccessingcontext'] = 'Cannot access context';
$string['errorfilecannotbecopied'] = 'Error cannot copy file $a.';
$string['errorfilecannotbemoved'] = 'Error cannot move file $a.';
$string['errorfileschanged'] = 'Error files linked to from questions have changed since form was displayed.';
$string['exportcategory'] = 'Export category';
$string['filesareasite']= 'the site files area';
$string['filesareacourse']= 'the course files area';
$string['filestomove']= 'Move / copy files to $a?';
$string['fractionsnomax'] = 'One of the answers should have a score of 100%% so it is possible to get full marks for this question.';
$string['getcategoryfromfile'] = 'Get category from file';
$string['getcontextfromfile'] = 'Get context from file';
$string['ignorebroken'] = 'Ignore broken links';
$string['linkedfiledoesntexist'] = 'Linked file $a doesn\'t exist';
$string['makechildof'] = "Make Child of '\$a'";
$string['maketoplevelitem'] = 'Move to top level';
$string['missingimportantcode'] = 'This question type is missing important code: $a.';
$string['modified'] = 'Modified';
$string['move']= 'Move from $a and change links.';
$string['movecategory']= 'Move Category';
$string['movelinksonly']= 'Just change where links point to, do not move or copy files.';
$string['moveqtoanothercontext']= 'Move question to another context.';
$string['moveq']= 'Move question(s)';
$string['movingcategory']= 'Moving Category';
$string['movingcategoryandfiles']= 'Are you sure you want to move category {$a->name} and all child categories to context for \"{$a->contextto}\"?<br /> We have detected {$a->urlcount} files linked from questions in {$a->fromareaname}, would you like to copy or move these to {$a->toareaname}?';
$string['movingcategorynofiles']= 'Are you sure you want to move category \"{$a->name}\" and all child categories to context for \"{$a->contextto}\"?';
$string['movingquestions'] = 'Moving Questions and Any Files';
$string['movingquestionsandfiles']= 'Are you sure you want to move question(s) {$a->questions} to context for <strong>\"{$a->tocontext}\"</strong>?<br /> We have detected <strong>{$a->urlcount} files</strong> linked from these question(s) in {$a->fromareaname}, would you like to copy or move these to {$a->toareaname}?';
$string['movingquestionsnofiles']= 'Are you sure you want to move question(s) {$a->questions} to context for <strong>\"{$a->tocontext}\"</strong>?<br /> There are <strong>no files</strong> linked from these question(s) in {$a->fromareaname}.';
$string['needtochoosecat'] = 'You need to choose a category to move this question to or press \'cancel\'.';
$string['nopermissionadd'] = 'You don\'t have permission to add questions here.';
$string['noprobs'] = 'No problems found in your question database.';
$string['notenoughdatatoeditaquestion'] = 'Neither a question id, nor a category id and question type, was specified.';
$string['notenoughdatatomovequestions'] = 'You need to provide the question ids of questions you want to move.';
$string['permissionedit'] = 'Edit this question';
$string['permissionmove'] = 'Move this question';
$string['permissionsaveasnew'] = 'Save this as a new question';
$string['permissionto'] = 'You have permission to :';
$string['published'] = 'published';
$string['questionaffected'] = '<a href=\"$a->qurl\">Question \"$a->name\" ($a->qtype)</a> is in this question category but is also being used in <a href=\"$a->qurl\">quiz \"$a->quizname\"</a> in another course \"$a->coursename\".';
$string['questionbank'] = 'Question bank';
$string['questioncatsfor'] = 'Question Categories for \'$a\'';
$string['questiondoesnotexist'] = 'This question does not exist';
$string['questionuse'] = 'Use question in this activity';
$string['shareincontext'] = 'Share in context for $a';
$string['tofilecategory'] = 'Write category to file';
$string['tofilecontext'] = 'Write context to file';
$string['unknown'] = 'Unknown';
$string['unknownquestiontype'] = 'Unknown question type: $a.';
$string['unpublished'] = 'unpublished';
?>

View file

@ -10,6 +10,18 @@ $string['action'] = 'Action';
$string['adaptive'] = 'Adaptive mode';
$string['addcategory'] = 'Add category';
$string['addingquestions'] = 'This side of the page is where you manage your database of questions. Questions are stored in categories to help you keep them organised, and can be used by any quiz in your course or even other courses if you choose to \'publish\' them. <br /><br />After you select or create a question category you will be able to create or edit questions. You can select any of these questions to add to your quiz over on the other side of this page.';
$string['addingcalculated'] = 'Adding a Calculated question';
$string['addingdescription'] = 'Adding a Description';
$string['addingessay'] = 'Adding Essay';
$string['addingmatch'] = 'Adding a Matching Question';
$string['addingmultianswer'] = 'Adding Embedded Answers (Cloze)';
$string['addingmultichoice'] = 'Adding a Multiple Choice question';
$string['addingnumerical'] = 'Adding a Numerical question';
$string['addingquestion'] = 'Adding a question';
$string['addingrandom'] = 'Adding a Random Question';
$string['addingrandomsamatch'] = 'Adding a Random Short-Answer Matching question';
$string['addingshortanswer'] = 'Adding a Short-Answer question';
$string['addingtruefalse'] = 'Adding a True/False question';
$string['addquestions'] = 'Add questions';
$string['addquestionstoquiz'] = 'Add questions to current quiz';
$string['addrandom'] = 'Add $a random questions';
@ -404,7 +416,7 @@ $string['quiz:deleteattempts'] = 'Delete quiz attempts';
$string['quiz:emailconfirmsubmission'] = 'Get email confirmation when submitting';
$string['quiz:emailnotifysubmission'] = 'Get email notification of submissions';
$string['quiz:grade'] = 'Grade quizzes manually';
$string['quiz:ignoretimelimits'] = 'Ignores time limit on quizzes';
$string['quiz:ignoretimelimits'] = 'Ignores time limit on quizs';
$string['quiz:manage'] = 'Manage quizzes';
$string['quiz:preview'] = 'Preview quizzes';
$string['quiz:view'] = 'View quiz information';

View file

@ -3017,6 +3017,7 @@ function delete_course($courseid, $showfeedback = true) {
*/
function remove_course_contents($courseid, $showfeedback=true) {
include_once($CFG->libdir.'/questionlib.php');
global $CFG;
$result = true;
@ -3042,6 +3043,8 @@ function remove_course_contents($courseid, $showfeedback=true) {
if ($instances = get_records($modname, 'course', $course->id)) {
foreach ($instances as $instance) {
if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
/// Delete activity context questions and question categories
question_delete_activity($cm, $showfeedback);
delete_context(CONTEXT_MODULE, $cm->id);
}
if ($moddelete($instance->id)) {
@ -3187,7 +3190,6 @@ function remove_course_contents($courseid, $showfeedback=true) {
}
/// Delete questions and question categories
include_once($CFG->libdir.'/questionlib.php');
question_delete_course($course, $showfeedback);
/// Delete all roles and overiddes in the course context (but keep the course context)

View file

@ -87,6 +87,16 @@ define('QUESTION_PREVIEW_POPUP_OPTIONS', 'scrollbars=yes,resizable=yes,width=700
*/
define('QUESTION_ADAPTIVE', 1);
/**
* options used in forms that move files.
*
*/
define('QUESTION_FILENOTHINGSELECTED', 0);
define('QUESTION_FILEDONOTHING', 1);
define('QUESTION_FILECOPY', 2);
define('QUESTION_FILEMOVE', 3);
define('QUESTION_FILEMOVELINKSONLY', 4);
/**#@-*/
/// QTYPES INITIATION //////////////////
@ -402,7 +412,9 @@ function delete_question($questionid) {
}
// delete questiontype-specific data
if ($question = get_record('question', 'id', $questionid)) {
$question = get_record('question', 'id', $questionid);
question_require_capability_on($question, 'edit');
if ($question) {
if (isset($QTYPES[$question->qtype])) {
$QTYPES[$question->qtype]->delete_question($questionid);
}
@ -441,106 +453,42 @@ function delete_question($questionid) {
}
/**
* All non-used question categories and their questions are deleted and
* categories still used by other courses are moved to the site course.
* All question categories and their questions are deleted for this course.
*
* @param object $course an object representing the course
* @param object $mod an object representing the activity
* @param boolean $feedback to specify if the process must output a summary of its work
* @return boolean
*/
function question_delete_course($course, $feedback=true) {
global $CFG, $QTYPES;
//To detect if we have created the "container category"
$concatid = 0;
//The "container" category we'll create if we need if
$contcat = new object;
//To temporary store changes performed with parents
$parentchanged = array();
//To store feedback to be showed at the end of the process
$feedbackdata = array();
//Cache some strings
$strcatcontainer=get_string('containercategorycreated', 'quiz');
$strcatmoved = get_string('usedcategorymoved', 'quiz');
$strcatdeleted = get_string('unusedcategorydeleted', 'quiz');
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$categoriescourse = get_records('question_categories', 'contextid', $coursecontext->id, 'parent', 'id, parent, name');
if ($categories = get_records('question_categories', 'course', $course->id, 'parent', 'id, parent, name, course')) {
if ($categoriescourse) {
//Sort categories following their tree (parent-child) relationships
$categories = sort_categories_by_tree($categories);
//this will make the feedback more readable
$categoriescourse = sort_categories_by_tree($categoriescourse);
foreach ($categories as $cat) {
foreach ($categoriescourse as $category) {
//Get the full record
$category = get_record('question_categories', 'id', $cat->id);
//Check if the category is being used anywhere
if(question_category_isused($category->id, true)) {
//It's being used. Cannot delete it, so:
//Create a container category in SITEID course if it doesn't exist
if (!$concatid) {
$concat = new stdClass;
$concat->course = SITEID;
if (!isset($course->shortname)) {
$course->shortname = 'id=' . $course->id;
}
$concat->name = get_string('savedfromdeletedcourse', 'quiz', format_string($course->shortname));
$concat->info = $concat->name;
$concat->publish = 1;
$concat->stamp = make_unique_id_code();
$concatid = insert_record('question_categories', $concat);
//Fill feedback
$feedbackdata[] = array($concat->name, $strcatcontainer);
//Delete it completely (questions and category itself)
//deleting questions
if ($questions = get_records("question", "category", $category->id)) {
foreach ($questions as $question) {
delete_question($question->id);
}
//Move the category to the container category in SITEID course
$category->course = SITEID;
//Assign to container if the category hasn't parent or if the parent is wrong (not belongs to the course)
if (!$category->parent || !isset($categories[$category->parent])) {
$category->parent = $concatid;
}
//If it's being used, its publish field should be 1
$category->publish = 1;
//Let's update it
update_record('question_categories', $category);
//Save this parent change for future use
$parentchanged[$category->id] = $category->parent;
//Fill feedback
$feedbackdata[] = array($category->name, $strcatmoved);
} else {
//Category isn't being used so:
//Delete it completely (questions and category itself)
//deleting questions
if ($questions = get_records("question", "category", $category->id)) {
foreach ($questions as $question) {
delete_question($question->id);
}
delete_records("question", "category", $category->id);
}
//delete the category
delete_records('question_categories', 'id', $category->id);
//Save this parent change for future use
if (!empty($category->parent)) {
$parentchanged[$category->id] = $category->parent;
} else {
$parentchanged[$category->id] = $concatid;
}
//Update all its child categories to re-parent them to grandparent.
set_field ('question_categories', 'parent', $parentchanged[$category->id], 'parent', $category->id);
//Fill feedback
$feedbackdata[] = array($category->name, $strcatdeleted);
delete_records("question", "category", $category->id);
}
//delete the category
delete_records('question_categories', 'id', $category->id);
//Fill feedback
$feedbackdata[] = array($category->name, $strcatdeleted);
}
//Inform about changes performed if feedback is enabled
if ($feedback) {
@ -553,22 +501,67 @@ function question_delete_course($course, $feedback=true) {
return true;
}
function questionbank_navigation_tabs(&$row, $context, $querystring) {
global $CFG;
if (has_capability('moodle/question:manage', $context)) {
$row[] = new tabobject('questions', "$CFG->wwwroot/question/edit.php?$querystring", get_string('questions', 'quiz'), get_string('editquestions', "quiz"));
}
/**
* All question categories and their questions are deleted for this activity.
*
* @param object $cm the course module object representing the activity
* @param boolean $feedback to specify if the process must output a summary of its work
* @return boolean
*/
function question_delete_activity($cm, $feedback=true) {
//To store feedback to be showed at the end of the process
$feedbackdata = array();
if (has_capability('moodle/question:managecategory', $context)) {
$row[] = new tabobject('categories', "$CFG->wwwroot/question/category.php?$querystring", get_string('categories', 'quiz'), get_string('editqcats', 'quiz'));
}
//Cache some strings
$strcatdeleted = get_string('unusedcategorydeleted', 'quiz');
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if ($categoriesmods = get_records('question_categories', 'contextid', $modcontext->id, 'parent', 'id, parent, name')){
//Sort categories following their tree (parent-child) relationships
//this will make the feedback more readable
$categoriesmods = sort_categories_by_tree($categoriesmods);
if (has_capability('moodle/question:import', $context)) {
$row[] = new tabobject('import', "$CFG->wwwroot/question/import.php?$querystring", get_string('import', 'quiz'), get_string('importquestions', 'quiz'));
}
foreach ($categoriesmods as $category) {
if (has_capability('moodle/question:export', $context)) {
$row[] = new tabobject('export', "$CFG->wwwroot/question/export.php?$querystring", get_string('export', 'quiz'), get_string('exportquestions', 'quiz'));
//Delete it completely (questions and category itself)
//deleting questions
if ($questions = get_records("question", "category", $category->id)) {
foreach ($questions as $question) {
delete_question($question->id);
}
delete_records("question", "category", $category->id);
}
//delete the category
delete_records('question_categories', 'id', $category->id);
//Fill feedback
$feedbackdata[] = array($category->name, $strcatdeleted);
}
//Inform about changes performed if feedback is enabled
if ($feedback) {
$table = new stdClass;
$table->head = array(get_string('category','quiz'), get_string('action'));
$table->data = $feedbackdata;
print_table($table);
}
}
return true;
}
/**
* @param array $row tab objects
* @param question_edit_contexts $contexts object representing contexts available from this context
* @param string $querystring to append to urls
* */
function questionbank_navigation_tabs(&$row, $contexts, $querystring) {
global $CFG, $QUESTION_EDITTABCAPS;
$tabs = array(
'questions' =>array("$CFG->wwwroot/question/edit.php?$querystring", get_string('questions', 'quiz'), get_string('editquestions', 'quiz')),
'categories' =>array("$CFG->wwwroot/question/category.php?$querystring", get_string('categories', 'quiz'), get_string('editqcats', 'quiz')),
'import' =>array("$CFG->wwwroot/question/import.php?$querystring", get_string('import', 'quiz'), get_string('importquestions', 'quiz')),
'export' =>array("$CFG->wwwroot/question/export.php?$querystring", get_string('export', 'quiz'), get_string('exportquestions', 'quiz')));
foreach ($tabs as $tabname => $tabparams){
if ($contexts->have_one_edit_tab_cap($tabname)) {
$row[] = new tabobject($tabname, $tabparams[0], $tabparams[1], $tabparams[2]);
}
}
}
@ -1589,7 +1582,7 @@ function sort_categories_by_tree(&$categories, $id = 0, $level = 1) {
* @param int $depth the indent depth. Used in recursive calls.
* @return array a new array of categories, in the right order for the tree.
*/
function flatten_category_tree(&$categories, $id, $depth = 0) {
function flatten_category_tree(&$categories, $id, $depth = 0, $nochildrenof = -1) {
// Indent the name of this category.
$newcategories = array();
@ -1598,7 +1591,9 @@ function flatten_category_tree(&$categories, $id, $depth = 0) {
// Recursively indent the children.
foreach ($categories[$id]->childids as $childid) {
$newcategories = $newcategories + flatten_category_tree($categories, $childid, $depth + 1);
if ($childid != $nochildrenof){
$newcategories = $newcategories + flatten_category_tree($categories, $childid, $depth + 1, $nochildrenof);
}
}
// Remove the childids array that were temporarily added.
@ -1613,7 +1608,7 @@ function flatten_category_tree(&$categories, $id, $depth = 0) {
* @param array $categories An array of category objects, for example from the.
* @return array The formatted list of categories.
*/
function add_indented_names($categories) {
function add_indented_names($categories, $nochildrenof = -1) {
// Add an array to each category to hold the child category ids. This array will be removed
// again by flatten_category_tree(). It should not be used outside these two functions.
@ -1636,7 +1631,7 @@ function add_indented_names($categories) {
// Flatten the tree to and add the indents.
$newcategories = array();
foreach ($toplevelcategoryids as $id) {
$newcategories = $newcategories + flatten_category_tree($categories, $id);
$newcategories = $newcategories + flatten_category_tree($categories, $id, 0, $nochildrenof);
}
return $newcategories;
@ -1653,70 +1648,102 @@ function add_indented_names($categories) {
* @param integer $only_editable if true, exclude categories this user is not allowed to edit.
* @param integer $selected optionally, the id of a category to be selected by default in the dropdown.
*/
function question_category_select_menu($courseid, $published = false, $only_editable = false, $selected = "") {
$categoriesarray = question_category_options($courseid, $published, $only_editable);
function question_category_select_menu($contexts, $top = false, $currentcat = 0, $selected = "", $nochildrenof = -1) {
$categoriesarray = question_category_options($contexts, $top, $currentcat, false, $nochildrenof);
if ($selected) {
$nothing = '';
} else {
$nothing = 'choose';
}
choose_from_menu($categoriesarray, 'category', $selected, $nothing);
choose_from_menu_nested($categoriesarray, 'category', $selected, $nothing);
}
/**
* Get all the category objects, including a count of the number of questions in that category,
* for all the categories in the lists $contexts.
*
* @param mixed $contexts either a single contextid, or a comma-separated list of context ids.
* @param string $sortorder used as the ORDER BY clause in the select statement.
* @return array of category objects.
*/
function get_categories_for_contexts($contexts, $sortorder = 'parent, sortorder, name ASC') {
global $CFG;
return get_records_sql("
SELECT *, (SELECT count(1) FROM {$CFG->prefix}question q
WHERE c.id = q.category AND q.hidden='0' AND q.parent='0') as questioncount
FROM {$CFG->prefix}question_categories as c
WHERE c.contextid IN ($contexts)
ORDER BY $sortorder");
}
/**
* Output an array of question categories.
*
* Categories from this course and (optionally) published categories from other courses
* are included. Optionally, only categories the current user may edit can be included.
*
* @param integer $courseid the id of the course to get the categories for.
* @param integer $published if true, include publised categories from other courses.
* @param integer $only_editable if true, exclude categories this user is not allowed to edit.
* @return array The list of categories.
*/
function question_category_options($courseid, $published = false, $only_editable = false) {
function question_category_options($contexts, $top = false, $currentcat = 0, $popupform = false, $nochildrenof = -1) {
global $CFG;
// get sql fragment for published
$publishsql="";
if ($published) {
$publishsql = " OR publish = 1";
$pcontexts = array();
foreach($contexts as $context){
$pcontexts[] = $context->id;
}
$contextslist = join($pcontexts, ', ');
$categories = get_records_sql("
SELECT cat.*, c.shortname AS coursename
FROM {$CFG->prefix}question_categories cat, {$CFG->prefix}course c
WHERE c.id = cat.course AND (cat.course = $courseid $publishsql)
ORDER BY (CASE WHEN cat.course = $courseid THEN 0 ELSE 1 END), cat.parent, cat.sortorder, cat.name ASC");
$categories = add_indented_names($categories);
$categories = get_categories_for_contexts($contextslist);
$categories = question_add_context_in_key($categories);
if ($top){
$categories = question_add_tops($categories, $pcontexts);
}
$categories = add_indented_names($categories, $nochildrenof);
//sort cats out into different contexts
$categoriesarray = array();
foreach ($categories as $category) {
$cid = $category->id;
$cname = question_category_coursename($category, $courseid);
if ((!$only_editable) || has_capability('moodle/question:managecategory', get_context_instance(CONTEXT_COURSE, $category->course))) {
$categoriesarray[$cid] = $cname;
foreach ($pcontexts as $pcontext){
$contextstring = print_context_name(get_context_instance_by_id($pcontext), true, true);
foreach ($categories as $category) {
if ($category->contextid == $pcontext){
$cid = $category->id;
if ($currentcat!= $cid || $currentcat==0) {
$countstring = (!empty($category->questioncount))?"($category->questioncount)":'';
$categoriesarray[$contextstring][$cid] = $category->indentedname.$countstring;
}
}
}
}
return $categoriesarray;
if ($popupform){
$popupcats = array();
foreach ($categoriesarray as $contextstring => $optgroup){
$popupcats[] = '--'.$contextstring;
$popupcats = array_merge($popupcats, $optgroup);
$popupcats[] = '--';
}
return $popupcats;
} else {
return $categoriesarray;
}
}
/**
* If the category is not from this course, and it is a published category,
* then return the course category name with the course shortname appended in
* brackets. Otherwise, just return the category name.
*/
function question_category_coursename($category, $courseid = 0) {
$cname = (isset($category->indentedname)) ? $category->indentedname : $category->name;
if ($category->course != $courseid && $category->publish) {
if (!empty($category->coursename)) {
$coursename = $category->coursename;
} else {
$coursename = get_field('course', 'shortname', 'id', $category->course);
}
$cname .= " ($coursename)";
function question_add_context_in_key($categories){
$newcatarray = array();
foreach ($categories as $id => $category) {
$category->parent = "$category->parent,$category->contextid";
$category->id = "$category->id,$category->contextid";
$newcatarray["$id,$category->contextid"] = $category;
}
return $cname;
return $newcatarray;
}
function question_add_tops($categories, $pcontexts){
$topcats = array();
foreach ($pcontexts as $context){
$newcat = new object();
$newcat->id = "0,$context";
$newcat->name = get_string('top');
$newcat->parent = -1;
$newcat->contextid = $context;
$topcats["0,$context"] = $newcat;
}
//put topcats in at beginning of array - they'll be sorted into different contexts later.
return array_merge($topcats, $categories);
}
/**
@ -1733,67 +1760,8 @@ function question_categorylist($categoryid) {
return $categorylist;
}
/**
* find and/or create the category described by a delimited list
* e.g. tom/dick/harry
* @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='/', $courseid=0 ) {
$catpath = clean_param( $catpath,PARAM_PATH );
$catnames = explode( $delimiter, $catpath );
$parent = 0;
$category = null;
foreach ($catnames as $catname) {
if ($category = get_record( 'question_categories', 'name', $catname, 'course', $courseid, 'parent', $parent )) {
$parent = $category->id;
}
else {
// create the new category
$category = new object;
$category->course = $courseid;
$category->name = $catname;
$category->info = '';
$category->publish = false;
$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;
}
/**
* 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='/' ) {
$path = '';
do {
if (!$category = get_record( 'question_categories','id',$id )) {
print_error( "Error reading category record - $id" );
}
$name = $category->name;
$id = $category->parent;
if (!empty($path)) {
$path = "{$name}{$delimiter}{$path}";
}
else {
$path = $name;
}
} while ($id != 0);
return $path;
}
//===========================
// Import/Export Functions
@ -1887,5 +1855,213 @@ function default_export_filename($course,$category) {
return $export_name;
}
class context_to_string_translator{
/**
* @var array used to translate between contextids and strings for this context.
*/
var $contexttostringarray = array();
function context_to_string_translator($contexts){
$this->generate_context_to_string_array($contexts);
}
function context_to_string($contextid){
return $this->contexttostringarray[$contextid];
}
function string_to_context($contextname){
$contextid = array_search($contextname, $this->contexttostringarray);
return $contextid;
}
function generate_context_to_string_array($contexts){
if (!$this->contexttostringarray){
$catno = 1;
foreach ($contexts as $context){
switch ($context->contextlevel){
case CONTEXT_MODULE :
$contextstring = 'module';
break;
case CONTEXT_COURSE :
$contextstring = 'course';
break;
case CONTEXT_COURSECAT :
$contextstring = "cat$catno";
$catno++;
break;
case CONTEXT_SYSTEM :
$contextstring = 'system';
break;
}
$this->contexttostringarray[$context->id] = $contextstring;
}
}
}
}
/**
* Check capability on category
* @param mixed $question object or id
* @param string $cap 'add', 'edit', 'view', 'use', 'move'
* @param integer $cachecat useful to cache all question records in a category
* @return boolean this user has the capability $cap for this question $question?
*/
function question_has_capability_on($question, $cap, $cachecat = -1){
global $USER;
// these are capabilities on existing questions capabilties are
//set per category. Each of these has a mine and all version. Append 'mine' and 'all'
$question_questioncaps = array('edit', 'view', 'use', 'move');
static $questions = array();
static $categories = array();
static $cachedcat = array();
if ($cachecat != -1 && (array_search($cachecat, $cachedcat)!==FALSE)){
$questions += get_records('question', 'category', $cachecat);
$cachedcat[] = $cachecat;
}
if (!is_object($question)){
if (!isset($questions[$question])){
if (!$questions[$question] = get_record('question', 'id', $question)){
print_error('invalidcategory', 'quiz');
}
}
$question = $questions[$question];
}
if (!isset($categories[$question->category])){
if (!$categories[$question->category] = get_record('question_categories', 'id', $question->category)){
print_error('invalidcategory', 'quiz');
}
}
$category = $categories[$question->category];
if (array_search($cap, $question_questioncaps)!== FALSE){
if (!has_capability('moodle/question:'.$cap.'all', get_context_instance_by_id($category->contextid))){
if ($question->createdby == $USER->id){
return has_capability('moodle/question:'.$cap.'mine', get_context_instance_by_id($category->contextid));
} else {
return false;
}
} else {
return true;
}
} else {
return has_capability('moodle/question:'.$cap, get_context_instance_by_id($category->contextid));
}
}
/**
* Require capability on question.
*/
function question_require_capability_on($question, $cap){
if (!question_has_capability_on($question, $cap)){
print_error('nopermissions', '', '', $cap);
}
return true;
}
function question_file_links_base_url($courseid){
global $CFG;
$baseurl = preg_quote("$CFG->wwwroot/file.php", '!');
$baseurl .= '('.preg_quote('?file=', '!').')?';//may or may not
//be using slasharguments, accept either
$baseurl .= "/$courseid/";//course directory
return $baseurl;
}
/*
* Find all course / site files linked to in a piece of 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 files with keys being files.
*/
function question_find_file_links_from_html($html, $courseid){
global $CFG;
$baseurl = question_file_links_base_url($courseid);
$searchfor = '!'.
'(<\s*(a|img)\s[^>]*(href|src)\s*=\s*")'.$baseurl.'([^"]*)"'.
'|'.
'(<\s*(a|img)\s[^>]*(href|src)\s*=\s*\')'.$baseurl.'([^\']*)\''.
'!i';
$matches = array();
$no = preg_match_all($searchfor, $html, $matches);
if ($no){
$rawurls = array_filter(array_merge($matches[5], $matches[10]));//array_filter removes empty elements
//remove any links that point somewhere they shouldn't
foreach (array_keys($rawurls) as $rawurlkey){
if (!$cleanedurl = question_url_check($rawurls[$rawurlkey])){
unset($rawurls[$rawurlkey]);
} else {
$rawurls[$rawurlkey] = $cleanedurl;
}
}
$urls = array_flip($rawurls);// array_flip removes duplicate files
// and when we merge arrays will continue to automatically remove duplicates
} else {
$urls = array();
}
return $urls;
}
/*
* Check that url doesn't point anywhere it shouldn't
*
* @param $url string relative url within course files directory
* @return mixed boolean false if not OK or cleaned URL as string if OK
*/
function question_url_check($url){
global $CFG;
if ((substr(strtolower($url), 0, strlen($CFG->moddata)) == strtolower($CFG->moddata)) ||
(substr(strtolower($url), 0, 10) == 'backupdata')){
return false;
} else {
return clean_param($url, PARAM_PATH);
}
}
/*
* Find all course / site files linked to in a piece of 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 files with keys being files.
*/
function question_replace_file_links_in_html($html, $fromcourseid, $tocourseid, $url, $destination, &$changed){
global $CFG;
if ($CFG->slasharguments) { // Use this method if possible for better caching
$tourl = "$CFG->wwwroot/file.php/$tocourseid/$destination";
} else {
$tourl = "$CFG->wwwroot/file.php?file=/$tocourseid/$destination";
}
$fromurl = question_file_links_base_url($fromcourseid).preg_quote($url, '!');
$searchfor = array('!(<\s*(a|img)\s[^>]*(href|src)\s*=\s*")'.$fromurl.'(")!i',
'!(<\s*(a|img)\s[^>]*(href|src)\s*=\s*\')'.$fromurl.'(\')!i');
$newhtml = preg_replace($searchfor, '\\1'.$tourl.'\\5', $html);
if ($newhtml != $html){
$changed = true;
}
return $newhtml;
}
function get_filesdir_from_context($context){
switch ($context->contextlevel){
case CONTEXT_COURSE :
$courseid = $context->instanceid;
break;
case CONTEXT_MODULE :
$courseid = get_field('course_modules', 'course', 'id', $context->instanceid);
break;
case CONTEXT_COURSECAT :
case CONTEXT_SYSTEM :
$courseid = SITEID;
break;
default :
error('Unsupported contextlevel in category record!');
}
return $courseid;
}
?>