moodle/mod/workshop/lib.php
David Mudrak f6e8b31802 workshop: general cleanup
Moving stuff from lib.php into static methods in localib.php. Removing
things from workshop core that were implemented in a subplugin. Dropping
stuff related to features not to be implemented in 2.0.
2010-01-04 18:22:10 +00:00

607 lines
23 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library of interface functions and constants for module workshop
*
* All the core Moodle functions, neeeded to allow the module to work
* integrated in Moodle should be placed here.
*
* @package mod-workshop
* @copyright 2009 David Mudrak <david.mudrak@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Returns the information if the module supports a feature
*
* @see plugin_supports() in lib/moodlelib.php
* @param string $feature FEATURE_xx constant for requested feature
* @return mixed true if the feature is supported, null if unknown
*/
function workshop_supports($feature) {
switch($feature) {
case FEATURE_GRADE_HAS_GRADE: return true;
case FEATURE_GROUPS: return true;
case FEATURE_GROUPINGS: return true;
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_MOD_SUBPLUGINS: return array(
'workshopform' => 'mod/workshop/form',
'workshopallocation' => 'mod/workshop/allocation',
'workshopeval' => 'mod/workshop/eval',
);
default: return null;
}
}
/**
* Saves a new instance of the workshop into the database
*
* Given an object containing all the necessary data,
* (defined by the form in mod_form.php) this function
* will save a new instance and return the id number
* of the new instance.
*
* @param stdClass $data An object from the form in mod_form.php
* @return int The id of the newly inserted workshop record
*/
function workshop_add_instance($data) {
global $CFG, $DB;
require_once(dirname(__FILE__) . '/locallib.php');
$data->phase = workshop::PHASE_SETUP;
$data->timecreated = time();
$data->timemodified = $data->timecreated;
// insert the new record so we get the id
$data->id = $DB->insert_record('workshop', $data);
// we need to use context now, so we need to make sure all needed info is already in db
$cmid = $data->coursemodule;
$DB->set_field('course_modules', 'instance', $data->id, array('id' => $cmid));
$context = get_context_instance(CONTEXT_MODULE, $cmid);
// process the custom wysiwyg editors
if ($draftitemid = $data->instructauthorseditor['itemid']) {
$data->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'workshop_instructauthors',
0, workshop::instruction_editors_options($context), $data->instructauthorseditor['text']);
$data->instructauthorsformat = $data->instructauthorseditor['format'];
}
if ($draftitemid = $data->instructreviewerseditor['itemid']) {
$data->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'workshop_instructreviewers',
0, workshop::instruction_editors_options($context), $data->instructreviewerseditor['text']);
$data->instructreviewersformat = $data->instructreviewerseditor['format'];
}
// re-save the record with the replaced URLs in editor fields
$DB->update_record('workshop', $data);
return $data->id;
}
/**
* Given an object containing all the necessary data,
* (defined by the form in mod_form.php) this function
* will update an existing instance with new data.
*
* @param stdClass $data An object from the form in mod_form.php
* @return bool success
*/
function workshop_update_instance($data) {
global $CFG, $DB;
require_once(dirname(__FILE__) . '/locallib.php');
$data->timemodified = time();
$data->id = $data->instance;
// todo - if the grading strategy is being changed, we must replace all aggregated peer grades with nulls
// todo - if maximum grades are being changed, we should probably recalculate or invalidate them
$DB->update_record('workshop', $data);
$context = get_context_instance(CONTEXT_MODULE, $data->coursemodule);
// process the custom wysiwyg editors
if ($draftitemid = $data->instructauthorseditor['itemid']) {
$data->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'workshop_instructauthors',
0, workshop::instruction_editors_options($context), $data->instructauthorseditor['text']);
$data->instructauthorsformat = $data->instructauthorseditor['format'];
}
if ($draftitemid = $data->instructreviewerseditor['itemid']) {
$data->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'workshop_instructreviewers',
0, workshop::instruction_editors_options($context), $data->instructreviewerseditor['text']);
$data->instructreviewersformat = $data->instructreviewerseditor['format'];
}
// re-save the record with the replaced URLs in editor fields
return $DB->update_record('workshop', $data);
}
/**
* Given an ID of an instance of this module,
* this function will permanently delete the instance
* and any data that depends on it.
*
* @param int $id Id of the module instance
* @return boolean Success/Failure
*/
function workshop_delete_instance($id) {
global $DB;
if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
return false;
}
// delete all associated aggregations
$DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id));
// get the list of ids of all submissions
$submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id');
// get the list of all allocated assessments
$assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
// delete the associated records from the workshop core tables
$DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
$DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
$DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
// todo call the static clean-up methods of all available subplugins
// ...
// finally remove the workshop record itself
$DB->delete_records('workshop', array('id' => $workshop->id));
return true;
}
/**
* Return a small object with summary information about what a
* user has done with a given particular instance of this module
* Used for user activity reports.
* $return->time = the time they did it
* $return->info = a short text description
*
* @return null
* @todo Finish documenting this function
*/
function workshop_user_outline($course, $user, $mod, $workshop) {
$return = new stdClass();
$return->time = 0;
$return->info = '';
return $return;
}
/**
* Print a detailed representation of what a user has done with
* a given particular instance of this module, for user activity reports.
*
* @return boolean
* @todo Finish documenting this function
*/
function workshop_user_complete($course, $user, $mod, $workshop) {
return true;
}
/**
* Given a course and a time, this module should find recent activity
* that has occurred in workshop activities and print it out.
* Return true if there was output, or false is there was none.
*
* @return boolean
* @todo Finish documenting this function
*/
function workshop_print_recent_activity($course, $isteacher, $timestart) {
return false; // True if anything was printed, otherwise false
}
/**
* Function to be run periodically according to the moodle cron
* This function searches for things that need to be done, such
* as sending out mail, toggling flags etc ...
*
* @return boolean
* @todo Finish documenting this function
**/
function workshop_cron () {
return true;
}
/**
* Must return an array of user records (all data) who are participants
* for a given instance of workshop. Must include every user involved
* in the instance, independient of his role (student, teacher, admin...)
* See other modules as example.
*
* @param int $workshopid ID of an instance of this module
* @return mixed boolean/array of students
*/
function workshop_get_participants($workshopid) {
return false;
}
/**
* This function returns if a scale is being used by one workshop
* if it has support for grading and scales. Commented code should be
* modified if necessary. See forum, glossary or journal modules
* as reference.
*
* @param int $workshopid ID of an instance of this module
* @return mixed
* @todo Finish documenting this function
*/
function workshop_scale_used($workshopid, $scaleid) {
$return = false;
//$rec = get_record("workshop","id","$workshopid","scale","-$scaleid");
//
//if (!empty($rec) && !empty($scaleid)) {
// $return = true;
//}
return $return;
}
/**
* Checks if scale is being used by any instance of workshop.
* This function was added in 1.9
*
* This is used to find out if scale used anywhere
* @param $scaleid int
* @return boolean True if the scale is used by any workshop
*/
function workshop_scale_used_anywhere($scaleid) {
if ($scaleid and record_exists('workshop', 'grade', -$scaleid)) {
return true;
} else {
return false;
}
}
/**
* Execute post-install custom actions for the module
* This function was added in 1.9
*
* @return boolean true if success, false on error
*/
function workshop_install() {
return true;
}
/**
* Returns all other caps used in the module
*
* @return array
*/
function workshop_get_extra_capabilities() {
return array('moodle/site:accessallgroups');
}
////////////////////////////////////////////////////////////////////////////////
// File API //
////////////////////////////////////////////////////////////////////////////////
/**
* Returns the lists of all browsable file areas within the given module context
*
* The file area workshop_intro for the activity introduction field is added automatically
* by {@link file_browser::get_file_info_module()}
*
* @param stdClass $course
* @param stdClass $cm
* @param stdClass $context
* @return array of [(string)filearea] => (string)description
*/
function workshop_get_file_areas($course, $cm, $context) {
$areas = array();
if (has_capability('moodle/course:managefiles', $context)) {
$areas['workshop_instructauthors'] = get_string('areainstructauthors', 'workshop');
$areas['workshop_submission_content'] = get_string('areasubmissioncontent', 'workshop');
$areas['workshop_submission_attachment'] = get_string('areasubmissionattachment', 'workshop');
}
return $areas;
}
/**
* Serves the files from the workshop file areas
*
* Apart from module intro (handled by pluginfile.php automatically), workshop files may be
* media inserted into submission content (like images) and submission attachments. For these two,
* the fileareas workshop_submission_content and workshop_submission_attachment are used.
* The access rights to the files are checked here. The user must be either a peer-reviewer
* of the submission or have capability ... (todo) to access the submission files.
* Besides that, areas workshop_instructauthors and workshop_instructreviewers contain the media
* embedded using the mod_form.php.
*
* @param stdClass $course
* @param stdClass $cminfo
* @param stdClass $context
* @param string $filearea
* @param array $args
* @param bool $forcedownload
* @return void this should never return to the caller
*/
function workshop_pluginfile($course, $cminfo, $context, $filearea, array $args, $forcedownload) {
global $DB;
if (!$cminfo->uservisible) {
send_file_not_found();
}
if (!$cm = get_coursemodule_from_instance('workshop', $cminfo->instance, $course->id)) {
send_file_not_found();
}
require_login($course, true, $cm);
if ($filearea === 'workshop_instructauthors') {
// submission instructions may contain sensitive data
if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:submit'), $context)) {
send_file_not_found();
}
array_shift($args); // we do not use itemids here
$relativepath = '/' . implode('/', $args);
$fullpath = $context->id . $filearea . '0' . $relativepath; // beware, slashes are not used here!
$fs = get_file_storage();
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
$lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
// finally send the file
send_stored_file($file, $lifetime, 0);
}
if ($filearea === 'workshop_instructreviewers') {
// submission instructions may contain sensitive data
if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:peerassess'), $context)) {
send_file_not_found();
}
array_shift($args); // we do not use itemids here
$relativepath = '/' . implode('/', $args);
$fullpath = $context->id . $filearea . '0' . $relativepath; // beware, slashes are not used here!
$fs = get_file_storage();
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
$lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
// finally send the file
send_stored_file($file, $lifetime, 0);
}
// the following file areas are for the files embedded into the assessment forms
if (in_array($filearea, array(
'workshopform_comments_description',
'workshopform_accumulative_description',
'workshopform_numerrors_description',
'workshopform_rubric_description',
))) {
$itemid = (int)array_shift($args); // the id of the assessment form dimension
if (!$workshop = $DB->get_record('workshop', array('id' => $cminfo->instance))) {
send_file_not_found();
}
switch ($filearea) {
case 'workshopform_comments_description':
$dimension = $DB->get_record('workshopform_comments', array('id' => $itemid));
break;
case 'workshopform_accumulative_description':
$dimension = $DB->get_record('workshopform_accumulative', array('id' => $itemid));
break;
case 'workshopform_numerrors_description':
$dimension = $DB->get_record('workshopform_numerrors', array('id' => $itemid));
break;
case 'workshopform_rubric_description':
$dimension = $DB->get_record('workshopform_rubric', array('id' => $itemid));
break;
default:
$dimension = false;
}
if (empty($dimension)) {
send_file_not_found();
}
if ($workshop->id != $dimension->workshopid) {
// this should never happen but just in case
send_file_not_found();
}
// TODO now make sure the user is allowed to see the file
// (media embedded into the dimension description)
$fs = get_file_storage();
$relativepath = '/' . implode('/', $args);
$fullpath = $context->id . $filearea . $itemid . $relativepath;
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
return false;
}
// finally send the file
send_stored_file($file);
}
if ($filearea == 'workshop_submission_content' or $filearea == 'workshop_submission_attachment') {
$itemid = (int)array_shift($args);
if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid))) {
return false;
}
if (!$workshop = $DB->get_record('workshop', array('id' => $cminfo->instance))) {
return false;
}
// TODO now make sure the user is allowed to see the file
$fs = get_file_storage();
$relativepath = '/' . implode('/', $args);
$fullpath = $context->id . $filearea . $itemid . $relativepath;
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
return false;
}
// finally send the file
// these files are uploaded by students - forcing download for security reasons
send_stored_file($file, 0, 0, true);
}
return false;
}
/**
* File browsing support for workshop file areas
*
* @param stdClass $browser
* @param stdClass $areas
* @param stdClass $course
* @param stdClass $cm
* @param stdClass $context
* @param string $filearea
* @param int $itemid
* @param string $filepath
* @param string $filename
* @return stdClass file_info instance or null if not found
*/
function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
global $CFG, $DB;
if (!has_capability('moodle/course:managefiles', $context)) {
return null;
}
$fs = get_file_storage();
if ($filearea === 'workshop_submission_content' or $filearea === 'workshop_submission_attachment') {
if (is_null($itemid)) {
require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
}
// we are inside the submission container
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($context->id, $filearea, $itemid);
} else {
// not found
return null;
}
}
// let us display the author's name instead of itemid (submission id)
// todo some sort of caching should happen here
$sql = 'SELECT s.id, u.lastname, u.firstname
FROM {workshop_submissions} s
INNER JOIN {user} u ON (s.authorid = u.id)
WHERE s.workshopid = ?';
$params = array($cm->instance);
$authors = $DB->get_records_sql($sql, $params);
$urlbase = $CFG->wwwroot . '/pluginfile.php';
$topvisiblename = fullname($authors[$itemid]);
// do not allow manual modification of any files!
return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
}
/* todo was replaced by subplugins' areas
if ($filearea === 'workshop_dimension_description') {
// always only itemid 0 - TODO not true, review
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
$urlbase = $CFG->wwwroot.'/pluginfile.php';
if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($context->id, $filearea, 0);
} else {
// not found
return null;
}
}
return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
}
*/
if ($filearea === 'workshop_instructauthors') {
// always only itemid 0
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
$urlbase = $CFG->wwwroot.'/pluginfile.php';
if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($context->id, $filearea, 0);
} else {
// not found
return null;
}
}
return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
}
}
////////////////////////////////////////////////////////////////////////////////
// Navigation API //
////////////////////////////////////////////////////////////////////////////////
/**
* Extends the global navigation tree by adding workshop nodes if there is a relevant content
*
* This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
*
* @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
* @param stdClass $course
* @param stdClass $module
* @param stdClass $cm
*/
function workshop_extend_navigation(navigation_node $navref, stdClass $course, stdClass $module, stdClass $cm) {
global $CFG;
if (has_capability('mod/workshop:submit', $cm->context)) {
$url = new moodle_url($CFG->wwwroot.'/mod/workshop/submission.php', array('cmid' => $cm->id));
$mysubmissionkey = $navref->add(get_string('mysubmission', 'workshop'), $url);
$navref->get($mysubmissionkey)->mainnavonly = true;
}
}
/**
* Extends the settings navigation with the Workshop settings
* This function is called when the context for the page is a workshop module. This is not called by AJAX
* so it is safe to rely on the $PAGE.
*
* @param settings_navigation $settingsnav {@link settings_navigation}
* @param stdClass $module
* @return void|mixed The key to the modules branch
*/
function workshop_extend_settings_navigation(settings_navigation $settingsnav, stdClass $module=null) {
global $CFG, $PAGE;
$workshopkey = $settingsnav->add(get_string('workshopadministration', 'workshop'));
$workshopnode = $settingsnav->get($workshopkey);
$workshopnode->forceopen = true;
//$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) {
$url = new moodle_url($CFG->wwwroot . '/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id));
$workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING);
}
if (has_capability('mod/workshop:allocate', $PAGE->context)) {
$url = new moodle_url($CFG->wwwroot . '/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id));
$workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING);
}
}