|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00027 defined('MOODLE_INTERNAL') || die(); 00028 00032 abstract class gradingform_controller { 00033 00035 const DEFINITION_STATUS_NULL = 0; 00037 const DEFINITION_STATUS_DRAFT = 10; 00039 const DEFINITION_STATUS_READY = 20; 00040 00042 protected $context; 00043 00045 protected $component; 00046 00048 protected $area; 00049 00051 protected $areaid; 00052 00054 protected $definition = false; 00055 00057 private $graderange = null; 00058 00060 protected $hasactiveinstances = null; 00061 00070 public function __construct(stdClass $context, $component, $area, $areaid) { 00071 global $DB; 00072 00073 $this->context = $context; 00074 list($type, $name) = normalize_component($component); 00075 $this->component = $type.'_'.$name; 00076 $this->area = $area; 00077 $this->areaid = $areaid; 00078 00079 $this->load_definition(); 00080 } 00081 00085 public function get_context() { 00086 return $this->context; 00087 } 00088 00092 public function get_component() { 00093 return $this->component; 00094 } 00095 00099 public function get_area() { 00100 return $this->area; 00101 } 00102 00106 public function get_areaid() { 00107 return $this->areaid; 00108 } 00109 00118 public function is_form_defined() { 00119 return ($this->definition !== false); 00120 } 00121 00127 public function is_form_available() { 00128 return ($this->is_form_defined() && $this->definition->status == self::DEFINITION_STATUS_READY); 00129 } 00130 00136 public function is_shared_template() { 00137 return ($this->get_context()->id == context_system::instance()->id 00138 and $this->get_component() == 'core_grading'); 00139 } 00140 00149 public function is_own_form($userid = null) { 00150 global $USER; 00151 00152 if (!$this->is_form_defined()) { 00153 return null; 00154 } 00155 if (is_null($userid)) { 00156 $userid = $USER->id; 00157 } 00158 return ($this->definition->usercreated == $userid); 00159 } 00160 00167 public function form_unavailable_notification() { 00168 if ($this->is_form_available()) { 00169 return null; 00170 } 00171 return get_string('gradingformunavailable', 'grading'); 00172 } 00173 00180 public function get_editor_url(moodle_url $returnurl = null) { 00181 00182 $params = array('areaid' => $this->areaid); 00183 00184 if (!is_null($returnurl)) { 00185 $params['returnurl'] = $returnurl->out(false); 00186 } 00187 00188 return new moodle_url('/grade/grading/form/'.$this->get_method_name().'/edit.php', $params); 00189 } 00190 00201 public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $node=null) { 00202 // do not extend by default 00203 } 00204 00214 public function extend_navigation(global_navigation $navigation, navigation_node $node=null) { 00215 // do not extend by default 00216 } 00217 00224 public function get_definition($force = false) { 00225 if ($this->definition === false || $force) { 00226 $this->load_definition(); 00227 } 00228 return $this->definition; 00229 } 00230 00237 public function get_definition_copy(gradingform_controller $target) { 00238 00239 if (get_class($this) != get_class($target)) { 00240 throw new coding_exception('The source and copy controller mismatch'); 00241 } 00242 00243 if ($target->is_form_defined()) { 00244 throw new coding_exception('The target controller already contains a form definition'); 00245 } 00246 00247 $old = $this->get_definition(); 00248 // keep our id 00249 $new = new stdClass(); 00250 $new->copiedfromid = $old->id; 00251 $new->name = $old->name; 00252 // once we support files embedded into the description, we will want to 00253 // relink them into the new file area here (that is why we accept $target) 00254 $new->description = $old->description; 00255 $new->descriptionformat = $old->descriptionformat; 00256 $new->options = $old->options; 00257 $new->status = $old->status; 00258 00259 return $new; 00260 } 00261 00272 public function update_definition(stdClass $definition, $usermodified = null) { 00273 global $DB, $USER; 00274 00275 if (is_null($usermodified)) { 00276 $usermodified = $USER->id; 00277 } 00278 00279 if (!empty($this->definition->id)) { 00280 // prepare a record to be updated 00281 $record = new stdClass(); 00282 // populate it with scalar values from the passed definition structure 00283 foreach ($definition as $prop => $val) { 00284 if (is_array($val) or is_object($val)) { 00285 // probably plugin's data 00286 continue; 00287 } 00288 $record->{$prop} = $val; 00289 } 00290 // make sure we do not override some crucial values by accident 00291 if (!empty($record->id) and $record->id != $this->definition->id) { 00292 throw new coding_exception('Attempting to update other definition record.'); 00293 } 00294 $record->id = $this->definition->id; 00295 unset($record->areaid); 00296 unset($record->method); 00297 unset($record->timecreated); 00298 // set the modification flags 00299 $record->timemodified = time(); 00300 $record->usermodified = $usermodified; 00301 00302 $DB->update_record('grading_definitions', $record); 00303 00304 } else if ($this->definition === false) { 00305 // prepare a record to be inserted 00306 $record = new stdClass(); 00307 // populate it with scalar values from the passed definition structure 00308 foreach ($definition as $prop => $val) { 00309 if (is_array($val) or is_object($val)) { 00310 // probably plugin's data 00311 continue; 00312 } 00313 $record->{$prop} = $val; 00314 } 00315 // make sure we do not override some crucial values by accident 00316 if (!empty($record->id)) { 00317 throw new coding_exception('Attempting to create a new record while there is already one existing.'); 00318 } 00319 unset($record->id); 00320 $record->areaid = $this->areaid; 00321 $record->method = $this->get_method_name(); 00322 $record->timecreated = time(); 00323 $record->usercreated = $usermodified; 00324 $record->timemodified = $record->timecreated; 00325 $record->usermodified = $record->usercreated; 00326 if (empty($record->status)) { 00327 $record->status = self::DEFINITION_STATUS_DRAFT; 00328 } 00329 if (empty($record->descriptionformat)) { 00330 $record->descriptionformat = FORMAT_MOODLE; // field can not be empty 00331 } 00332 00333 $DB->insert_record('grading_definitions', $record); 00334 00335 } else { 00336 throw new coding_exception('Unknown status of the cached definition record.'); 00337 } 00338 } 00339 00345 public function get_formatted_description() { 00346 if (!isset($this->definition->description)) { 00347 return ''; 00348 } 00349 return format_text($this->definition->description, $this->definition->descriptionformat); 00350 } 00351 00361 public function get_current_instance($raterid, $itemid, $idonly = false) { 00362 global $DB; 00363 $params = array( 00364 'definitionid' => $this->definition->id, 00365 'itemid' => $itemid, 00366 'status1' => gradingform_instance::INSTANCE_STATUS_ACTIVE, 00367 'status2' => gradingform_instance::INSTANCE_STATUS_NEEDUPDATE); 00368 $select = 'definitionid=:definitionid and itemid=:itemid and (status=:status1 or status=:status2)'; 00369 if (false /* TODO $manager->allow_multiple_raters() */) { 00370 $select .= ' and raterid=:raterid'; 00371 $params['raterid'] = $raterid; 00372 } 00373 if ($idonly) { 00374 if ($current = $DB->get_record_select('grading_instances', $select, $params, 'id', IGNORE_MISSING)) { 00375 return $current->id; 00376 } 00377 } else { 00378 if ($current = $DB->get_record_select('grading_instances', $select, $params, '*', IGNORE_MISSING)) { 00379 return $this->get_instance($current); 00380 } 00381 } 00382 return null; 00383 } 00384 00392 public function get_active_instances($itemid) { 00393 global $DB; 00394 $conditions = array('definitionid' => $this->definition->id, 00395 'itemid' => $itemid, 00396 'status' => gradingform_instance::INSTANCE_STATUS_ACTIVE); 00397 $records = $DB->get_recordset('grading_instances', $conditions); 00398 $rv = array(); 00399 foreach ($records as $record) { 00400 $rv[] = $this->get_instance($record); 00401 } 00402 return $rv; 00403 } 00404 00411 public function has_active_instances() { 00412 global $DB; 00413 if (empty($this->definition->id)) { 00414 return false; 00415 } 00416 if ($this->hasactiveinstances === null) { 00417 $conditions = array('definitionid' => $this->definition->id, 00418 'status' => gradingform_instance::INSTANCE_STATUS_ACTIVE); 00419 $this->hasactiveinstances = $DB->record_exists('grading_instances', $conditions); 00420 } 00421 return $this->hasactiveinstances; 00422 } 00423 00430 protected function get_instance($instance) { 00431 global $DB; 00432 if (is_scalar($instance)) { 00433 // instance id is passed as parameter 00434 $instance = $DB->get_record('grading_instances', array('id' => $instance), '*', MUST_EXIST); 00435 } 00436 if ($instance) { 00437 $class = 'gradingform_'. $this->get_method_name(). '_instance'; 00438 return new $class($this, $instance); 00439 } 00440 return null; 00441 } 00442 00453 public function create_instance($raterid, $itemid = null) { 00454 00455 // first find if there is already an active instance for this itemid 00456 if ($itemid && $current = $this->get_current_instance($raterid, $itemid)) { 00457 return $this->get_instance($current->copy($raterid, $itemid)); 00458 } else { 00459 $class = 'gradingform_'. $this->get_method_name(). '_instance'; 00460 return $this->get_instance($class::create_new($this->definition->id, $raterid, $itemid)); 00461 } 00462 } 00463 00474 public function get_or_create_instance($instanceid, $raterid, $itemid) { 00475 global $DB; 00476 if ($instanceid && 00477 $instance = $DB->get_record('grading_instances', array('id' => $instanceid, 'raterid' => $raterid, 'itemid' => $itemid), '*', IGNORE_MISSING)) { 00478 return $this->get_instance($instance); 00479 } 00480 return $this->create_instance($raterid, $itemid); 00481 } 00482 00492 abstract public function render_preview(moodle_page $page); 00493 00500 public function delete_definition() { 00501 global $DB; 00502 00503 if (!$this->is_form_defined()) { 00504 // nothing to do 00505 return; 00506 } 00507 00508 // firstly, let the plugin delete everything from their own tables 00509 $this->delete_plugin_definition(); 00510 // then, delete all instances left 00511 $DB->delete_records('grading_instances', array('definitionid' => $this->definition->id)); 00512 // finally, delete the main definition record 00513 $DB->delete_records('grading_definitions', array('id' => $this->definition->id)); 00514 00515 $this->definition = false; 00516 } 00517 00524 public static function sql_search_from_tables($gdid) { 00525 return ''; 00526 } 00527 00538 public static function sql_search_where($token) { 00539 00540 $subsql = array(); 00541 $params = array(); 00542 00543 return array($subsql, $params); 00544 } 00545 00547 00555 protected function load_definition() { 00556 global $DB; 00557 $this->definition = $DB->get_record('grading_definitions', array( 00558 'areaid' => $this->areaid, 00559 'method' => $this->get_method_name()), '*', IGNORE_MISSING); 00560 } 00561 00567 abstract protected function delete_plugin_definition(); 00568 00573 protected function get_method_name() { 00574 if (preg_match('/^gradingform_([a-z][a-z0-9_]*[a-z0-9])_controller$/', get_class($this), $matches)) { 00575 return $matches[1]; 00576 } else { 00577 throw new coding_exception('Invalid class name'); 00578 } 00579 } 00580 00591 public function render_grade($page, $itemid, $gradinginfo, $defaultcontent, $cangrade) { 00592 return $defaultcontent; 00593 } 00594 00602 public final function set_grade_range(array $graderange) { 00603 $this->graderange = $graderange; 00604 } 00605 00611 public final function get_grade_range() { 00612 if (empty($this->graderange)) { 00613 return array(); 00614 } 00615 return $this->graderange; 00616 } 00617 } 00618 00625 abstract class gradingform_instance { 00626 const INSTANCE_STATUS_ACTIVE = 1; 00627 const INSTANCE_STATUS_NEEDUPDATE = 2; 00628 const INSTANCE_STATUS_INCOMPLETE = 0; 00629 const INSTANCE_STATUS_ARCHIVE = 3; 00630 00632 protected $data; 00634 protected $controller; 00635 00642 public function __construct($controller, $data) { 00643 $this->data = (object)$data; 00644 $this->controller = $controller; 00645 } 00646 00655 public static function create_new($definitionid, $raterid, $itemid) { 00656 global $DB; 00657 $instance = new stdClass(); 00658 $instance->definitionid = $definitionid; 00659 $instance->raterid = $raterid; 00660 $instance->itemid = $itemid; 00661 $instance->status = self::INSTANCE_STATUS_INCOMPLETE; 00662 $instance->timemodified = time(); 00663 $instance->feedbackformat = FORMAT_MOODLE; 00664 $instanceid = $DB->insert_record('grading_instances', $instance); 00665 return $instanceid; 00666 } 00667 00677 public function copy($raterid, $itemid) { 00678 global $DB; 00679 $data = (array)$this->data; // Cast to array to make a copy 00680 unset($data['id']); 00681 $data['raterid'] = $raterid; 00682 $data['itemid'] = $itemid; 00683 $data['timemodified'] = time(); 00684 $data['status'] = self::INSTANCE_STATUS_INCOMPLETE; 00685 $instanceid = $DB->insert_record('grading_instances', $data); 00686 return $instanceid; 00687 } 00688 00695 public function get_current_instance() { 00696 if ($this->get_status() == self::INSTANCE_STATUS_ACTIVE || $this->get_status() == self::INSTANCE_STATUS_NEEDUPDATE) { 00697 return $this; 00698 } 00699 return $this->get_controller()->get_current_instance($this->data->raterid, $this->data->itemid); 00700 } 00701 00707 public function get_controller() { 00708 return $this->controller; 00709 } 00710 00717 public function get_data($key) { 00718 if (isset($this->data->$key)) { 00719 return $this->data->$key; 00720 } 00721 return null; 00722 } 00723 00729 public function get_id() { 00730 return $this->get_data('id'); 00731 } 00732 00738 public function get_status() { 00739 return $this->get_data('status'); 00740 } 00741 00745 protected function make_active() { 00746 global $DB; 00747 if ($this->data->status == self::INSTANCE_STATUS_ACTIVE) { 00748 // already active 00749 return; 00750 } 00751 if (empty($this->data->itemid)) { 00752 throw new coding_exception('You cannot mark active the grading instance without itemid'); 00753 } 00754 $currentid = $this->get_controller()->get_current_instance($this->data->raterid, $this->data->itemid, true); 00755 if ($currentid && $currentid != $this->get_id()) { 00756 $DB->update_record('grading_instances', array('id' => $currentid, 'status' => self::INSTANCE_STATUS_ARCHIVE)); 00757 } 00758 $DB->update_record('grading_instances', array('id' => $this->get_id(), 'status' => self::INSTANCE_STATUS_ACTIVE)); 00759 $this->data->status = self::INSTANCE_STATUS_ACTIVE; 00760 } 00761 00769 public function cancel() { 00770 global $DB; 00771 // TODO what if we happen delete the ACTIVE instance, shall we rollback to the last ARCHIVE? or throw an exception? 00772 // TODO create cleanup cron 00773 $DB->delete_records('grading_instances', array('id' => $this->get_id())); 00774 } 00775 00783 public function update($elementvalue) { 00784 global $DB; 00785 $newdata = new stdClass(); 00786 $newdata->id = $this->get_id(); 00787 $newdata->timemodified = time(); 00788 if (isset($elementvalue['itemid']) && $elementvalue['itemid'] != $this->data->itemid) { 00789 $newdata->itemid = $elementvalue['itemid']; 00790 } 00791 // TODO also update: rawgrade, feedback, feedbackformat 00792 $DB->update_record('grading_instances', $newdata); 00793 foreach ($newdata as $key => $value) { 00794 $this->data->$key = $value; 00795 } 00796 } 00797 00803 abstract public function get_grade(); 00804 00815 public function submit_and_get_grade($elementvalue, $itemid) { 00816 $elementvalue['itemid'] = $itemid; 00817 $this->update($elementvalue); 00818 $this->make_active(); 00819 return $this->get_grade(); 00820 } 00821 00822 00859 abstract function render_grading_element($page, $gradingformelement); 00860 00867 public function validate_grading_element($elementvalue) { 00868 return true; 00869 } 00870 00879 public function default_validation_error_message() { 00880 return ''; 00881 } 00882 }