]> git.mjollnir.org Git - moodle.git/commitdiff
wip
authorDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 17:54:36 +0000 (17:54 +0000)
committerDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 17:54:36 +0000 (17:54 +0000)
mod/workshop/db/access.php
mod/workshop/db/install.xml
mod/workshop/grading/accumulative/edit_form.php
mod/workshop/grading/accumulative/strategy.php
mod/workshop/grading/lib.php
mod/workshop/grading/noerrors/edit_form.php
mod/workshop/grading/noerrors/strategy.php

index 13caa3a70ab82495ff26bb1da80daea8ab32fd61..f075aafcd4109b8403c5e3ba2b1a689de6646fd3 100644 (file)
@@ -74,7 +74,7 @@ $mod_workshop_capabilities = array(
     ),
 
     'mod/workshop:editdimensions' => array(
-
+        'riskbitmask' => RISK_XSS,  // can embed flash and javascript into wysiwyg
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
         'legacy' => array(
index 8555b3e2d6307ff1128ec12204e3bd01e9de0a71..6fa1de73ed53bc6180c9eea0604bfc0b1b9e9072 100644 (file)
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="description"/>
         <FIELD NAME="description" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="The description of the dimension" PREVIOUS="id" NEXT="descriptionformat"/>
-        <FIELD NAME="descriptionformat" TYPE="int" LENGTH="3" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="The format of the description field" PREVIOUS="description" NEXT="grade0"/>
-        <FIELD NAME="grade0" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="The word describing the negative evaluation (like Poor, Missing, Absent, etc.). If NULL, it defaults to a translated string False" PREVIOUS="descriptionformat" NEXT="grade1"/>
+        <FIELD NAME="descriptionformat" TYPE="int" LENGTH="3" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="The format of the description field" PREVIOUS="description" NEXT="descriptiontrust"/>
+        <FIELD NAME="descriptiontrust" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="descriptionformat" NEXT="grade0"/>
+        <FIELD NAME="grade0" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="The word describing the negative evaluation (like Poor, Missing, Absent, etc.). If NULL, it defaults to a translated string False" PREVIOUS="descriptiontrust" NEXT="grade1"/>
         <FIELD NAME="grade1" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="A word for possitive evaluation (like Good, Present, OK etc). If NULL, it defaults to a translated string True" PREVIOUS="grade0" NEXT="weight"/>
         <FIELD NAME="weight" TYPE="int" LENGTH="5" NOTNULL="false" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" COMMENT="Weight of this dimension" PREVIOUS="grade1"/>
       </FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="workshopid"/>
         <FIELD NAME="workshopid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The id of the workshop" PREVIOUS="id" NEXT="nonegative"/>
         <FIELD NAME="nonegative" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Number of negative responses given by the reviewer" PREVIOUS="workshopid" NEXT="grade"/>
-        <FIELD NAME="grade" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Percentual grade 0..100 for this number of negative responses" PREVIOUS="nonegative"/>
+        <FIELD NAME="grade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="Percentual grade 0..100 for this number of negative responses" PREVIOUS="nonegative"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="workshop_fk"/>
-        <KEY NAME="workshop_fk" TYPE="foreign" FIELDS="workshopid" REFTABLE="workshop" REFFIELDS="id" PREVIOUS="primary"/>
+        <KEY NAME="workshop_fk" TYPE="foreign" FIELDS="workshopid" REFTABLE="workshop" REFFIELDS="id" PREVIOUS="primary" NEXT="nonegative_uq"/>
+        <KEY NAME="nonegative_uq" TYPE="unique" FIELDS="workshopid, nonegative" COMMENT="Number of negative must be unique within the given workshop" PREVIOUS="workshop_fk"/>
       </KEYS>
     </TABLE>
     <TABLE NAME="workshop_forms_rubric" COMMENT="The assessment dimensions definitions of Rubric grading strategy forms" PREVIOUS="workshop_forms_noerrors_map" NEXT="workshop_forms_rubric_levels">
index 97b0f81bebc6368d50e2813f6494714e49081715..79bb7e9dcb943ecf908fca87855d492e1399b344 100644 (file)
@@ -56,9 +56,9 @@ class workshop_edit_accumulative_strategy_form extends workshop_edit_strategy_fo
 
         for ($i = 0; $i < $norepeats; $i++) {
             $mform->addElement('header', 'dimension'.$i, get_string('dimensionnumberaccumulative', 'workshop', $i+1));
-            $mform->addElement('hidden', 'dimensionid__idx_'.$i);   // the id in workshop_forms_accumulative
+            $mform->addElement('hidden', 'dimensionid__idx_'.$i);   // the id in workshop_forms
             $mform->addElement('editor', 'description__idx_'.$i.'_editor', get_string('dimensiondescription', 'workshop'),
-                                    array('cols' => 20), $descriptionopts);
+                                    '', $descriptionopts);
             $mform->addElement('modgrade', 'grade__idx_'.$i, get_string('dimensionmaxgrade','workshop'), null, true);
             $mform->setDefault('grade__idx_'.$i, 10);
             $mform->addElement('select', 'weight__idx_'.$i, get_string('dimensionweight', 'workshop'), $weights);
index 3b18e1a46f0455a22f219448c89787eed145609e..26e4be99efc0d54ca9ade186bc8b8dfddf898ba8 100644 (file)
@@ -44,15 +44,17 @@ class workshop_accumulative_strategy implements workshop_strategy {
     /**
      * Constructor
      *
-     * @param object $workshop The workshop instance record
+     * @param workshop $workshop The workshop instance record
      * @return void
      */
-    public function __construct($workshop) {
+    public function __construct(workshop $workshop) {
         $this->workshop         = $workshop;
         $this->dimensions       = $this->load_fields();
         $this->descriptionopts  = array('trusttext' => true, 'subdirs' => false, 'maxfiles' => -1);
     }
 
+/// Public API
+
     /**
      * @return string
      */
@@ -98,45 +100,6 @@ class workshop_accumulative_strategy implements workshop_strategy {
         return new workshop_edit_accumulative_strategy_form($actionurl, $customdata, 'post', '', $attributes);
     }
 
-    /**
-     * Loads the fields of the assessment form currently used in this workshop
-     *
-     * @return array definition of assessment dimensions
-     */
-    protected function load_fields() {
-        global $DB;
-
-        $sql = "SELECT master.id,dim.description,dim.descriptionformat,dim.grade,dim.weight
-                  FROM {workshop_forms} master
-            INNER JOIN {workshop_forms_accumulative} dim ON (dim.id=master.localid)
-                 WHERE master.workshopid = :workshopid AND master.strategy = :strategy
-                 ORDER BY master.sort";
-        $params = array("workshopid" => $this->workshop->id, "strategy" => $this->workshop->strategy);
-
-        return $DB->get_records_sql($sql, $params);
-    }
-
-    /**
-     * Maps the dimension data from DB to the form fields
-     *
-     * @param array $raw Array of raw dimension records as returned by {@link load_fields()}
-     * @return array Array of fields data to be used by the mform set_data
-     */
-    protected function prepare_form_fields(array $raw) {
-
-        $formdata = new object();
-        $key = 0;
-        foreach ($raw as $dimension) {
-            $formdata->{'dimensionid__idx_' . $key}             = $dimension->id; // master id, not the local one!
-            $formdata->{'description__idx_' . $key}             = $dimension->description;
-            $formdata->{'description__idx_' . $key.'format'}    = $dimension->descriptionformat;
-            $formdata->{'grade__idx_' . $key}                   = $dimension->grade;
-            $formdata->{'weight__idx_' . $key}                  = $dimension->weight;
-            $key++;
-        }
-        return $formdata;
-    }
-
     /**
      * Save the assessment dimensions into database
      *
@@ -146,10 +109,10 @@ class workshop_accumulative_strategy implements workshop_strategy {
      * The passed data object are the raw data returned by the get_data().
      *
      * @uses $DB
-     * @param object $data Raw data returned by the dimension editor form
+     * @param stdClass $data Raw data returned by the dimension editor form
      * @return void
      */
-    public function save_edit_strategy_form(object $data) {
+    public function save_edit_strategy_form(stdClass $data) {
         global $DB, $PAGE;
 
         if (!isset($data->strategyname) || ($data->strategyname != $this->name())) {
@@ -160,9 +123,9 @@ class workshop_accumulative_strategy implements workshop_strategy {
         $norepeats  = $data->norepeats;
 
         $data       = $this->prepare_database_fields($data);
-        $masters    = $data->forms;                 // data to be saved into workshop_forms
-        $locals     = $data->forms_accumulative;    // data to be saved into workshop_forms_accumulative
-        $todelete   = array();                      // master ids to be deleted
+        $masters    = $data->forms;         // data to be saved into workshop_forms
+        $locals     = $data->accumulative;  // data to be saved into workshop_forms_accumulative
+        $todelete   = array();              // master ids to be deleted
 
         for ($i=0; $i < $norepeats; $i++) {
             $local  = $locals[$i];
@@ -187,72 +150,11 @@ class workshop_accumulative_strategy implements workshop_strategy {
             // re-save with correct path to embeded media files
             $local = file_postupdate_standard_editor($local, 'description', $this->descriptionopts,
                 $PAGE->context, 'workshop_dimension_description', $master->id);
-            $DB->update_record('workshop_forms_accumulative', $local);
+            $DB->update_record("workshop_forms_accumulative", $local);
         }
         $this->delete_dimensions($todelete);
     }
 
-    /**
-     * Deletes dimensions and removes embedded media from its descriptions
-     *
-     * todo we may check that there are no assessments done using these dimensions and probably remove them
-     *
-     * @param array $masterids
-     * @return void
-     */
-    protected function delete_dimensions($masterids) {
-        global $DB, $PAGE;
-
-        $masters    = $DB->get_records_list("workshop_forms", "id", $masterids, "", "id,localid");
-        $masterids  = array_keys($masters);  // now contains only those really existing
-        $localids   = array();
-        $fs         = get_file_storage();
-
-        foreach ($masters as $itemid => $master) {
-            $fs->delete_area_files($PAGE->context->id, 'workshop_dimension_description', $itemid);
-            $localids[] = $master->localid;
-        }
-        $DB->delete_records_list("workshop_forms_accumulative", "id", $localids);
-        $DB->delete_records_list("workshop_forms", "id", $masterids);
-    }
-
-    /**
-     * Prepares data returned by {@link workshop_edit_accumulative_strategy_form} so they can be saved into database
-     *
-     * It automatically adds some columns into every record. The sorting is
-     * done by the order of the returned array and starts with 1.
-     * Called internally from {@link save_edit_strategy_form()} only. Could be private but
-     * keeping protected for unit testing purposes.
-     *
-     * @param object $raw Raw data returned by mform
-     * @return array Array of objects to be inserted/updated in DB
-     */
-    protected function prepare_database_fields(object $raw) {
-        global $PAGE;
-
-        $cook                       = new object();   // to be returned
-        $cook->forms                = array();          // to be stored in {workshop_forms}
-        $cook->forms_accumulative   = array();          // to be stored in {workshop_forms_accumulative}
-
-        for ($i = 0; $i < $raw->norepeats; $i++) {
-            $cook->forms_accumulative[$i] = new object();
-
-            $fieldname = 'description__idx_'.$i.'_editor';
-            $cook->forms_accumulative[$i]->description_editor   = isset($raw->$fieldname) ? $raw->$fieldname : null;
-            $fieldname = 'grade__idx_'.$i;
-            $cook->forms_accumulative[$i]->grade                = isset($raw->$fieldname) ? $raw->$fieldname : null;
-            $fieldname = 'weight__idx_'.$i;
-            $cook->forms_accumulative[$i]->weight               = isset($raw->$fieldname) ? $raw->$fieldname : null;
-
-            $cook->forms[$i]                = new object();
-            $cook->forms[$i]->id            = isset($raw->{'dimensionid__idx_'.$i}) ? $raw->{'dimensionid__idx_'.$i} : null;
-            $cook->forms[$i]->workshopid    = $this->workshop->id;
-            $cook->forms[$i]->sort          = $i + 1;
-            $cook->forms[$i]->strategy      = 'accumulative';
-        }
-        return $cook;
-    }
-
     /**
      * Factory method returning an instance of an assessment form
      *
@@ -339,6 +241,104 @@ class workshop_accumulative_strategy implements workshop_strategy {
         return $this->update_peer_grade($assessment);
     }
 
+/// Internal methods
+
+    /**
+     * Loads the fields of the assessment form currently used in this workshop
+     *
+     * @return array definition of assessment dimensions
+     */
+    protected function load_fields() {
+        global $DB;
+
+        $sql = "SELECT master.id,dim.description,dim.descriptionformat,dim.grade,dim.weight
+                  FROM {workshop_forms} master
+            INNER JOIN {workshop_forms_accumulative} dim ON (dim.id=master.localid)
+                 WHERE master.workshopid = :workshopid AND master.strategy = :strategy
+                 ORDER BY master.sort";
+        $params = array("workshopid" => $this->workshop->id, "strategy" => $this->workshop->strategy);
+
+        return $DB->get_records_sql($sql, $params);
+    }
+
+    /**
+     * Maps the dimension data from DB to the form fields
+     *
+     * @param array $raw Array of raw dimension records as returned by {@link load_fields()}
+     * @return array Array of fields data to be used by the mform set_data
+     */
+    protected function prepare_form_fields(array $raw) {
+
+        $formdata = new object();
+        $key = 0;
+        foreach ($raw as $dimension) {
+            $formdata->{'dimensionid__idx_' . $key}             = $dimension->id; // master id, not the local one!
+            $formdata->{'description__idx_' . $key}             = $dimension->description;
+            $formdata->{'description__idx_' . $key.'format'}    = $dimension->descriptionformat;
+            $formdata->{'grade__idx_' . $key}                   = $dimension->grade;
+            $formdata->{'weight__idx_' . $key}                  = $dimension->weight;
+            $key++;
+        }
+        return $formdata;
+    }
+
+    /**
+     * Deletes dimensions and removes embedded media from its descriptions
+     *
+     * todo we may check that there are no assessments done using these dimensions and probably remove them
+     *
+     * @param array $masterids
+     * @return void
+     */
+    protected function delete_dimensions($masterids) {
+        global $DB, $PAGE;
+
+        $masters    = $DB->get_records_list("workshop_forms", "id", $masterids, "", "id,localid");
+        $masterids  = array_keys($masters);  // now contains only those really existing
+        $localids   = array();
+        $fs         = get_file_storage();
+
+        foreach ($masters as $itemid => $master) {
+            $fs->delete_area_files($PAGE->context->id, 'workshop_dimension_description', $itemid);
+            $localids[] = $master->localid;
+        }
+        $DB->delete_records_list("workshop_forms_accumulative", "id", $localids);
+        $DB->delete_records_list("workshop_forms", "id", $masterids);
+    }
+
+    /**
+     * Prepares data returned by {@link workshop_edit_accumulative_strategy_form} so they can be saved into database
+     *
+     * It automatically adds some columns into every record. The sorting is
+     * done by the order of the returned array and starts with 1.
+     * Called internally from {@link save_edit_strategy_form()} only. Could be private but
+     * keeping protected for unit testing purposes.
+     *
+     * @param stdClass $raw Raw data returned by mform
+     * @return array Array of objects to be inserted/updated in DB
+     */
+    protected function prepare_database_fields(stdClass $raw) {
+        global $PAGE;
+
+        $cook               = new object(); // to be returned
+        $cook->forms        = array();      // to be stored in {workshop_forms}
+        $cook->accumulative = array();      // to be stored in {workshop_forms_accumulative}
+
+        for ($i = 0; $i < $raw->norepeats; $i++) {
+            $cook->forms[$i]                = new object();
+            $cook->forms[$i]->id            = $raw->{'dimensionid__idx_'.$i};
+            $cook->forms[$i]->workshopid    = $this->workshop->id;
+            $cook->forms[$i]->sort          = $i + 1;
+            $cook->forms[$i]->strategy      = 'accumulative';
+
+            $cook->accumulative[$i]         = new object();
+            $cook->accumulative[$i]->description_editor = $raw->{'description__idx_'.$i.'_editor'};
+            $cook->accumulative[$i]->grade  = $raw->{'grade__idx_'.$i};
+            $cook->accumulative[$i]->weight = $raw->{'weight__idx_'.$i};
+        }
+        return $cook;
+    }
+
     /**
      * Returns the list of current grades filled by the reviewer
      *
index 6a6105581cc1dad61ce9b0f22cabb63592aa4d3b..9d4355b72705a230984e6145100b2cd2d3e5ef79 100644 (file)
@@ -57,10 +57,10 @@ interface workshop_strategy {
      * to be evaluated. Each dimension consists of a set of form fields. Strategy-specific information
      * are saved in workshop_forms_{strategyname} tables.
      *
-     * @param object $data Raw data as returned by the form editor
+     * @param stdClass $data Raw data as returned by the form editor
      * @return void
      */
-    public function save_edit_strategy_form(object $data);
+    public function save_edit_strategy_form(stdClass $data);
 
     /**
      * Factory method returning an instance of an assessment form
index c67db1590686fa0eddbb8ccad8c67eb96aa99fb2..3b4a92e868160c39be0abeac4ac1c17fd1f9063b 100644 (file)
@@ -44,36 +44,32 @@ class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form {
      */
     protected function definition_inner(&$mform) {
 
-        $workshopconfig = get_config('workshop');
-        $weights = workshop_get_dimension_weights();
+        $workshopconfig     = get_config('workshop');
+        $norepeats          = $this->_customdata['norepeats'];          // number of dimensions to display
+        $descriptionopts    = $this->_customdata['descriptionopts'];    // wysiwyg fields options
+        $current            = $this->_customdata['current'];            // current data to be set
 
-        $repeated = array();
-        $repeated[] = $mform->createElement('hidden', 'dimensionid', 0);
-        $repeated[] = $mform->createElement('header', 'dimension',
-                                                get_string('dimensionnumbernoerrors', 'workshop', '{no}'));
-        $repeated[] = $mform->createElement('htmleditor', 'description',
-                                                get_string('dimensiondescription', 'workshop'), array());
-        $repeated[] = $mform->createElement('text', 'grade0', get_string('noerrorsgrade0', 'workshop'), array('size'=>'15'));
-        $repeated[] = $mform->createElement('text', 'grade1', get_string('noerrorsgrade1', 'workshop'), array('size'=>'15'));
-        $repeated[] = $mform->createElement('select', 'weight', get_string('dimensionweight', 'workshop'), $weights);
+        $mform->addElement('hidden', 'norepeats', $norepeats);
+        // value not to be overridden by submitted value
+        $mform->setConstants(array('norepeats' => $norepeats));
 
-        $repeatedoptions = array();
-        $repeatedoptions['description']['type'] = PARAM_CLEANHTML;
-        $repeatedoptions['description']['helpbutton'] = array('dimensiondescription',
-                                                            get_string('dimensiondescription', 'workshop'), 'workshop');
-        $repeatedoptions['grade0']['type'] = PARAM_TEXT;
-        $repeatedoptions['grade0']['default'] = $workshopconfig->noerrorsgrade0;
-        $repeatedoptions['grade1']['type'] = PARAM_TEXT;
-        $repeatedoptions['grade1']['default'] = $workshopconfig->noerrorsgrade1;
-        $repeatedoptions['weight']['default'] = 1;
+        $weights = workshop_get_dimension_weights();
+
+        for ($i = 0; $i < $norepeats; $i++) {
+            $mform->addElement('header', 'dimension'.$i, get_string('dimensionnumbernoerrors', 'workshop', $i+1));
+            $mform->addElement('hidden', 'dimensionid__idx_'.$i);   // the id in workshop_forms
+            $mform->addElement('editor', 'description__idx_'.$i.'_editor', get_string('dimensiondescription', 'workshop'),
+                                    '', $descriptionopts);
+            $mform->addElement('text', 'grade0__idx_'.$i, get_string('noerrorsgrade0', 'workshop'), array('size'=>'15'));
+            $mform->setDefault('grade0__idx_'.$i, $workshopconfig->noerrorsgrade0);
+            $mform->setType('grade0__idx_'.$i, PARAM_TEXT);
+            $mform->addElement('text', 'grade1__idx_'.$i, get_string('noerrorsgrade1', 'workshop'), array('size'=>'15'));
+            $mform->setDefault('grade1__idx_'.$i, $workshopconfig->noerrorsgrade1);
+            $mform->setType('grade1__idx_'.$i, PARAM_TEXT);
+            $mform->addElement('select', 'weight__idx_'.$i, get_string('dimensionweight', 'workshop'), $weights);
+            $mform->setDefault('weight__idx_'.$i, 1);
+        }
 
-        $numofdimensionstoadd   = 2;
-        $numofinitialdimensions = 3;
-        $numofdisplaydimensions = max($this->strategy->get_number_of_dimensions() + $numofdimensionstoadd,
-                                                                                            $numofinitialdimensions);
-        $numofdisplaydimensions = $this->repeat_elements($repeated, $numofdisplaydimensions,  $repeatedoptions,
-                                                    'numofdimensions', 'adddimensions', $numofdimensionstoadd,
-                                                    get_string('addmoredimensionsnoerrors', 'workshop', $numofdimensionstoadd));
         $mform->addElement('header', 'mappingheader', get_string('noerrorsgrademapping', 'workshop'));
         $mform->addElement('static', 'mappinginfo', get_string('noerrorsmaperror', 'workshop'),
                                                             get_string('noerrorsmapgrade', 'workshop'));
@@ -83,16 +79,22 @@ class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form {
             $percents[$i] = get_string('percents', 'workshop', $i);
         }
         $mform->addElement('static', 'mappingzero', 0, get_string('percents', 'workshop', 100));
-        $mform->addElement('hidden', 'map[0]', 100);
-        for ($i = 1; $i <= $numofdisplaydimensions; $i++) {
+        $mform->addElement('hidden', 'map__idx_0', 100);
+        for ($i = 1; $i <= $norepeats; $i++) {
             $selects = array();
-            $selects[] = $mform->createElement('select', "map[$i]", $i, $percents);
-            $selects[] = $mform->createElement('static', "mapdefault[$i]", '',
-                                        get_string('percents', 'workshop', floor(100 - $i * 100 / $numofdisplaydimensions)));
-            $mform->addGroup($selects, "grademapping$i", $i, array(' '), false);
-            $mform->setDefault("map[$i]", '');
+            $selects[] = $mform->createElement('select', 'map__idx_'.$i, $i, $percents);
+            $selects[] = $mform->createElement('static', 'mapdefault__idx_'.$i, '',
+                                        get_string('percents', 'workshop', floor(100 - $i * 100 / $norepeats)));
+            $mform->addGroup($selects, 'grademapping'.$i, $i, array(' '), false);
+            $mform->setDefault('map__idx_'.$i, '');
         }
 
+        $mform->registerNoSubmitButton('noadddims');
+        $mform->addElement('submit', 'noadddims', get_string('addmoredimensionsaccumulative', 'workshop',
+                                                                    WORKSHOP_STRATEGY_ADDDIMS));
+        $mform->closeHeaderBefore('noadddims');
+        $this->set_data($current);
+
     }
 
 }
index ccf0736a6c5bb35b436e6c7829fb1f32539cfc38..e83da7c3013764d6f8c3c99868aa6a76df1498bd 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-require_once(dirname(dirname(__FILE__)) . '/strategy.php'); // parent class
+require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition
 
 /**
  * "Number of errors" grading strategy logic.
  */
-class workshop_noerrors_strategy extends workshop_base_strategy {
+class workshop_noerrors_strategy implements workshop_strategy {
 
-    public function load_form() {
-        global $DB;
+    /** @var workshop the parent workshop instance */
+    protected $workshop;
 
-        $dims = $DB->get_records('workshop_forms_' . $this->name(), array('workshopid' => $this->workshop->id), 'sort');
-        $maps = $DB->get_records('workshop_forms_noerrors_map', array('workshopid' => $this->workshop->id), 'nonegative');
-        $this->nodimensions = count($dims);
-        return $this->_cook_database_records($dims, $maps);
-    }
+    /** @var array definition of the assessment form fields */
+    protected $dimensions = null;
+
+    /** @var array mapping of the number of errors to a grade */
+    protected $mappings = null;
+
+    /** @var array options for dimension description fields */
+    protected $descriptionopts;
 
     /**
-     * Transpones the dimension data from DB so the assessment form editor can be populated by set_data
+     * Constructor
      *
-     * Called internally from load_form(). Could be private but keeping protected
-     * for unit testing purposes.
+     * @param workshop $workshop The workshop instance record
+     * @return void
+     */
+    public function __construct(workshop $workshop) {
+        $this->workshop         = $workshop;
+        $this->dimensions       = $this->load_fields();
+        $this->mappings         = $this->load_mappings();
+        $this->descriptionopts  = array('trusttext' => true, 'subdirs' => false, 'maxfiles' => -1);
+    }
+
+/// Public API methods
+
+    /**
+     * @return string
+     */
+    public function name() {
+        return 'noerrors';
+    }
+
+    /**
+     * Factory method returning an instance of an assessment form editor class
      *
-     * @param array $dims Array of raw dimension records as fetched by get_record()
-     * @param array $maps Array of grade mappings
-     * @return array Object to be used by the mform set_data
+     * @param $actionurl URL of form handler, defaults to auto detect the current url
      */
-    protected function _cook_database_records(array $dims, array $maps) {
+    public function get_edit_strategy_form($actionurl=null) {
+        global $CFG;    // needed because the included files use it
+        global $PAGE;
 
-        $formdata = array();
+        require_once(dirname(__FILE__) . '/edit_form.php');
 
-        // cook dimensions
-        $key = 0;
-        foreach ($dims as $dimension) {
-            $formdata['dimensionid[' . $key . ']']       = $dimension->id;
-            $formdata['description[' . $key . ']']       = $dimension->description;
-            $formdata['descriptionformat[' . $key . ']'] = $dimension->descriptionformat;
-            $formdata['grade0[' . $key . ']']            = $dimension->grade0;
-            $formdata['grade1[' . $key . ']']            = $dimension->grade1;
-            $formdata['weight[' . $key . ']']            = $dimension->weight;
-            $key++;
+        $fields             = $this->prepare_form_fields($this->dimensions, $this->mappings);
+        $nodimensions       = count($this->dimensions);
+        $norepeatsdefault   = max($nodimensions + WORKSHOP_STRATEGY_ADDDIMS, WORKSHOP_STRATEGY_MINDIMS);
+        $norepeats          = optional_param('norepeats', $norepeatsdefault, PARAM_INT);    // number of dimensions
+        $noadddims          = optional_param('noadddims', '', PARAM_ALPHA);                 // shall we add more?
+        if ($noadddims) {
+            $norepeats += WORKSHOP_STRATEGY_ADDDIMS;
         }
 
-        // cook grade mappings
-        foreach ($maps as $map) {
-            $formdata['map[' . $map->nonegative . ']'] = $map->grade;
+        // prepare the embeded files
+        for ($i = 0; $i < $nodimensions; $i++) {
+            // prepare all editor elements
+            $fields = file_prepare_standard_editor($fields, 'description__idx_'.$i, $this->descriptionopts,
+                $PAGE->context, 'workshop_dimension_description', $fields->{'dimensionid__idx_'.$i});
         }
 
-        return (object)$formdata;
+        $customdata = array();
+        $customdata['workshop'] = $this->workshop;
+        $customdata['strategy'] = $this;
+        $customdata['norepeats'] = $norepeats;
+        $customdata['descriptionopts'] = $this->descriptionopts;
+        $customdata['current']  = $fields;
+        $attributes = array('class' => 'editstrategyform');
+
+        return new workshop_edit_noerrors_strategy_form($actionurl, $customdata, 'post', '', $attributes);
     }
 
     /**
-     * Save the definition of a "Number of errors" grading form
+     * Save the assessment dimensions into database
      *
-     * The dimensions data are stored in workshop_forms_noerrors. The data that map the
-     * number of errors to a grade are saved into workshop_forms_noerrors_map.
+     * Saves data into the main strategy form table. If the record->id is null or zero,
+     * new record is created. If the record->id is not empty, the existing record is updated. Records with
+     * empty 'description' field are removed from database.
+     * The passed data object are the raw data returned by the get_data().
      *
      * @uses $DB
-     * @param object $data Raw data returned by the dimension editor form
+     * @param stdClass $data Raw data returned by the dimension editor form
      * @return void
      */
-    public function save_form(object $data) {
-        global $DB;
+    public function save_edit_strategy_form(stdClass $data) {
+        global $DB, $PAGE;
 
         if (!isset($data->strategyname) || ($data->strategyname != $this->name())) {
             // the workshop strategy has changed since the form was opened for editing
             throw new moodle_exception('strategyhaschanged', 'workshop');
         }
+        $workshopid = $data->workshopid;
+        $norepeats  = $data->norepeats;
 
-        // save the dimensions data
-        $dims = $this->_cook_form_data($data);
-        $todelete = array();
-        foreach ($dims as $record) {
-            if (empty($record->description)) {
-                if (!empty($record->id)) {
+        $data       = $this->prepare_database_fields($data);
+        $masters    = $data->forms;     // data to be saved into {workshop_forms}
+        $locals     = $data->noerrors;  // data to be saved into {workshop_forms_noerrors}
+        $mappings   = $data->mappings;  // data to be saved into {workshop_forms_noerrors_map}
+        $todelete   = array();          // master ids to be deleted
+
+        for ($i=0; $i < $norepeats; $i++) {
+            $local  = $locals[$i];
+            $master = $masters[$i];
+            if (empty($local->description_editor['text'])) {
+                if (!empty($master->id)) {
                     // existing record with empty description - to be deleted
-                    $todelete[] = $record->id;
+                    $todelete[] = $master->id;
                 }
                 continue;
             }
-            if (empty($record->id)) {
+            if (empty($master->id)) {
                 // new field
-                $record->id = $DB->insert_record('workshop_forms_' . $this->name(), $record);
+                $local->id          = $DB->insert_record("workshop_forms_noerrors", $local);
+                $master->localid    = $local->id;
+                $master->id         = $DB->insert_record("workshop_forms", $master);
             } else {
                 // exiting field
-                $DB->update_record('workshop_forms_' . $this->name(), $record);
+                $DB->update_record("workshop_forms", $master);
+                $local->id = $DB->get_field("workshop_forms", "localid", array("id" => $master->id), MUST_EXIST);
             }
+            // re-save with correct path to embeded media files
+            $local = file_postupdate_standard_editor($local, 'description', $this->descriptionopts,
+                $PAGE->context, 'workshop_dimension_description', $master->id);
+            $DB->update_record("workshop_forms_noerrors", $local);
         }
-        // delete dimensions if the teacher removed the description
-        $DB->delete_records_list('workshop_forms_' . $this->name(), 'id', $todelete);
+        $this->delete_dimensions($todelete);
 
         // re-save the mappings
         $current  = array();
@@ -159,30 +203,255 @@ class workshop_noerrors_strategy extends workshop_base_strategy {
     }
 
     /**
-     * Prepares dimensions data returned by mform so they can be saved into database
+     * Factory method returning an instance of an assessment form
+     *
+     * @param moodle_url $actionurl URL of form handler, defaults to auto detect the current url
+     * @param string $mode          Mode to open the form in: preview/assessment
+     */
+    public function get_assessment_form(moodle_url $actionurl=null, $mode='preview', object $assessment=null) {
+        global $CFG;    // needed because the included files use it
+        global $PAGE;
+        global $DB;
+        require_once(dirname(__FILE__) . '/assessment_form.php');
+
+        $fields         = $this->prepare_form_fields($this->dimensions, $this->mappings);
+        $nodimensions   = count($this->dimensions);
+
+        // rewrite URLs to the embeded files
+        for ($i = 0; $i < $nodimensions; $i++) {
+            $fields->{'description__idx_'.$i} = file_rewrite_pluginfile_urls($fields->{'description__idx_'.$i},
+                'pluginfile.php', $PAGE->context->id, 'workshop_dimension_description', $fields->{'dimensionid__idx_'.$i});
+        }
+
+        if ('assessment' === $mode and !empty($assessment)) {
+            // load the previously saved assessment data
+            $grades = $this->reindex_grades_by_dimension($this->get_current_assessment_data($assessment));
+            $current = new object();
+            for ($i = 0; $i < $nodimensions; $i++) {
+                $dimid = $fields->{'dimensionid__idx_'.$i};
+                if (isset($grades[$dimid])) {
+                    $current->{'gradeid__idx_'.$i}      = $grades[$dimid]->id;
+                    $current->{'grade__idx_'.$i}        = $grades[$dimid]->grade;
+                    $current->{'peercomment__idx_'.$i}  = $grades[$dimid]->peercomment;
+                }
+            }
+        }
+
+        // set up the required custom data common for all strategies
+        $customdata['strategy'] = $this;
+        $customdata['mode']     = $mode;
+
+        // set up strategy-specific custom data
+        $customdata['nodims']   = $nodimensions;
+        $customdata['fields']   = $fields;
+        $customdata['current']  = isset($current) ? $current : null;
+        $attributes = array('class' => 'assessmentform noerrors');
+
+        return new workshop_noerrors_assessment_form($actionurl, $customdata, 'post', '', $attributes);
+    }
+
+    /**
+     * Saves the filled assessment
+     *
+     * This method processes data submitted using the form returned by {@link get_assessment_form()}
+     *
+     * @param object $assessment Assessment being filled
+     * @param object $data       Raw data as returned by the assessment form
+     * @return float|null        Percentual grade for submission as suggested by the peer
+     */
+    public function save_assessment(object $assessment, object $data) {
+        global $DB;
+
+        if (!isset($data->strategyname) || ($data->strategyname != $this->name())) {
+            // the workshop strategy has changed since the form was opened for editing
+            throw new moodle_exception('strategyhaschanged', 'workshop');
+        }
+        if (!isset($data->nodims)) {
+            throw coding_expection('You did not send me the number of assessment dimensions to process');
+        }
+        for ($i = 0; $i < $data->nodims; $i++) {
+            $grade = new object();
+            $grade->id = $data->{'gradeid__idx_' . $i};
+            $grade->assessmentid = $assessment->id;
+            $grade->dimensionid = $data->{'dimensionid__idx_' . $i};
+            $grade->grade = $data->{'grade__idx_' . $i};
+            $grade->peercomment = $data->{'peercomment__idx_' . $i};
+            $grade->peercommentformat = FORMAT_HTML;
+            if (empty($grade->id)) {
+                // new grade
+                $grade->id = $DB->insert_record('workshop_grades', $grade);
+            } else {
+                // updated grade
+                $DB->update_record('workshop_grades', $grade);
+            }
+        }
+        return $this->update_peer_grade($assessment);
+    }
+
+    /**
+     * Save the definition of a "Number of errors" grading form
+     *
+     * The dimensions data are stored in workshop_forms_noerrors. The data that map the
+     * number of errors to a grade are saved into workshop_forms_noerrors_map.
+     *
+     * @uses $DB
+     * @param object $data Raw data returned by the dimension editor form
+     * @return void
+     */
+    public function save_form(object $data) {
+        global $DB;
+
+        if (!isset($data->strategyname) || ($data->strategyname != $this->name())) {
+            // the workshop strategy has changed since the form was opened for editing
+            throw new moodle_exception('strategyhaschanged', 'workshop');
+        }
+
+        // save the dimensions data
+        $dims = $this->_cook_form_data($data);
+        $todelete = array();
+        foreach ($dims as $record) {
+            if (empty($record->description)) {
+                if (!empty($record->id)) {
+                    // existing record with empty description - to be deleted
+                    $todelete[] = $record->id;
+                }
+                continue;
+            }
+            if (empty($record->id)) {
+                // new field
+                $record->id = $DB->insert_record('workshop_forms_' . $this->name(), $record);
+            } else {
+                // exiting field
+                $DB->update_record('workshop_forms_' . $this->name(), $record);
+            }
+        }
+        // delete dimensions if the teacher removed the description
+        $DB->delete_records_list('workshop_forms_' . $this->name(), 'id', $todelete);
+
+    }
+
+/// Internal methods
+
+    /**
+     * Loads the fields of the assessment form currently used in this workshop
+     *
+     * @return array definition of assessment dimensions
+     */
+    protected function load_fields() {
+        global $DB;
+
+        $sql = "SELECT master.id,dim.description,dim.descriptionformat,dim.grade0,dim.grade1,dim.weight
+                  FROM {workshop_forms} master
+            INNER JOIN {workshop_forms_noerrors} dim ON (dim.id=master.localid)
+                 WHERE master.workshopid = :workshopid AND master.strategy = :strategy
+                 ORDER BY master.sort";
+        $params = array("workshopid" => $this->workshop->id, "strategy" => $this->workshop->strategy);
+
+        return $DB->get_records_sql($sql, $params);
+    }
+
+    /**
+     * Loads the mappings of the number of errors to the grade
+     *
+     * @return array of records
+     */
+    protected function load_mappings() {
+        global $DB;
+        return $DB->get_records("workshop_forms_noerrors_map", array("workshopid" => $this->workshop->id), "nonegative",
+                                "nonegative,grade"); // we can use nonegative as key here as it must be unique within workshop
+    }
+
+    /**
+     * Prepares the database data to be used by the mform
+     *
+     * @param array $dims Array of raw dimension records as returned by {@link load_fields()}
+     * @param array $maps Array of raw mapping records as returned by {@link load_mappings()}
+     * @return array Array of fields data to be used by the mform set_data
+     */
+    protected function prepare_form_fields(array $dims, array $maps) {
+
+        $formdata = new object();
+        $key = 0;
+        foreach ($dims as $dimension) {
+            $formdata->{'dimensionid__idx_' . $key}             = $dimension->id; // master id, not the local one!
+            $formdata->{'description__idx_' . $key}             = $dimension->description;
+            $formdata->{'description__idx_' . $key.'format'}    = $dimension->descriptionformat;
+            $formdata->{'description__idx_' . $key.'trust'}     = $dimension->descriptiontrust;
+            $formdata->{'grade0__idx_' . $key}                  = $dimension->grade0;
+            $formdata->{'grade1__idx_' . $key}                  = $dimension->grade1;
+            $formdata->{'weight__idx_' . $key}                  = $dimension->weight;
+            $key++;
+        }
+
+        foreach ($maps as $nonegative => $map) {
+            $formdata->{'map__idx_' . $nonegative} = $map->grade;
+        }
+
+        return $formdata;
+    }
+
+    /**
+     * Deletes dimensions and removes embedded media from its descriptions
+     *
+     * todo we may check that there are no assessments done using these dimensions and probably remove them
+     *
+     * @param array $masterids
+     * @return void
+     */
+    protected function delete_dimensions($masterids) {
+        global $DB, $PAGE;
+
+        $masters    = $DB->get_records_list("workshop_forms", "id", $masterids, "", "id,localid");
+        $masterids  = array_keys($masters);  // now contains only those really existing
+        $localids   = array();
+        $fs         = get_file_storage();
+
+        foreach ($masters as $itemid => $master) {
+            $fs->delete_area_files($PAGE->context->id, 'workshop_dimension_description', $itemid);
+            $localids[] = $master->localid;
+        }
+        $DB->delete_records_list("workshop_forms_noerrors", "id", $localids);
+        $DB->delete_records_list("workshop_forms", "id", $masterids);
+    }
+
+    /**
+     * Prepares data returned by {@link workshop_edit_noerrors_strategy_form} so they can be saved into database
      *
      * It automatically adds some columns into every record. The sorting is
      * done by the order of the returned array and starts with 1.
-     * Called internally from save_form() only. Could be private but
+     * Called internally from {@link save_edit_strategy_form()} only. Could be private but
      * keeping protected for unit testing purposes.
      *
-     * @param object $raw Raw data returned by mform
+     * @param stdClass $raw Raw data returned by mform
      * @return array Array of objects to be inserted/updated in DB
      */
-    protected function _cook_form_data(object $raw) {
-
-        $cook = array();
-
-        for ($k = 0; $k < $raw->numofdimensions; $k++) {
-            $cook[$k]                    = new object();
-            $cook[$k]->id                = isset($raw->dimensionid[$k]) ? $raw->dimensionid[$k] : null;
-            $cook[$k]->workshopid        = $this->workshop->id;
-            $cook[$k]->sort              = $k + 1;
-            $cook[$k]->description       = isset($raw->description[$k]) ? $raw->description[$k] : null;
-            $cook[$k]->descriptionformat = FORMAT_HTML;
-            $cook[$k]->grade0            = isset($raw->grade0[$k]) ? $raw->grade0[$k] : null;
-            $cook[$k]->grade1            = isset($raw->grade1[$k]) ? $raw->grade1[$k] : null;
-            $cook[$k]->weight            = isset($raw->weight[$k]) ? $raw->weight[$k] : null;
+    protected function prepare_database_fields(stdClass $raw) {
+        global $PAGE;
+
+        $cook           = new object(); // to be returned
+        $cook->forms    = array();      // to be stored in {workshop_forms}
+        $cook->noerrors = array();      // to be stored in {workshop_forms_noerrors}
+        $cook->mappings = array();      // to be stored in {workshop_forms_noerrors_map}
+
+        for ($i = 0; $i < $raw->norepeats; $i++) {
+            $cook->forms[$i]                = new object();
+            $cook->forms[$i]->id            = $raw->{'dimensionid__idx_'.$i};
+            $cook->forms[$i]->workshopid    = $this->workshop->id;
+            $cook->forms[$i]->sort          = $i + 1;
+            $cook->forms[$i]->strategy      = 'noerrors';
+
+            $cook->noerrors[$i]             = new object();
+            $cook->noerrors[$i]->description_editor = $raw->{'description__idx_'.$i.'_editor'};
+            $cook->noerrors[$i]->grade0     = $raw->{'grade0__idx_'.$i};
+            $cook->noerrors[$i]->grade1     = $raw->{'grade1__idx_'.$i};
+            $cook->noerrors[$i]->weight     = $raw->{'weight__idx_'.$i};
+
+            if (empty($raw->{'map__idx_'.$i})) {
+                $cook->mappings[$i]         = null;
+            } else {
+                $cook->mappings[$i]         = new object();
+                $cook->mappings[$i]->grade  = $raw->{'map__idx_'.$i};
+            }
         }
         return $cook;
     }