Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/grade/grading/form/lib.php
Go to the documentation of this file.
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 }
 All Data Structures Namespaces Files Functions Variables Enumerations