diff --git a/mod/data/backup/moodle2/backup_data_stepslib.php b/mod/data/backup/moodle2/backup_data_stepslib.php index 5fe9ee6f87a..385e66bb070 100644 --- a/mod/data/backup/moodle2/backup_data_stepslib.php +++ b/mod/data/backup/moodle2/backup_data_stepslib.php @@ -50,7 +50,7 @@ class backup_data_activity_structure_step extends backup_activity_structure_step $fields = new backup_nested_element('fields'); $field = new backup_nested_element('field', array('id'), array( - 'type', 'name', 'description', 'param1', 'param2', + 'type', 'name', 'description', 'required', 'param1', 'param2', 'param3', 'param4', 'param5', 'param6', 'param7', 'param8', 'param9', 'param10')); diff --git a/mod/data/db/install.xml b/mod/data/db/install.xml index 37b46f1d798..aeddfb62e3f 100644 --- a/mod/data/db/install.xml +++ b/mod/data/db/install.xml @@ -1,5 +1,5 @@ - @@ -54,6 +54,7 @@ + @@ -93,11 +94,11 @@ - - - - - + + + + + diff --git a/mod/data/db/upgrade.php b/mod/data/db/upgrade.php index 61c57d57640..d818ddbd616 100644 --- a/mod/data/db/upgrade.php +++ b/mod/data/db/upgrade.php @@ -133,11 +133,21 @@ function xmldb_data_upgrade($oldversion) { // Moodle v2.7.0 release upgrade line. // Put any upgrade step following this. + if ($oldversion < 2014051201) { + // Define field required to be added to data_fields. + $table = new xmldb_table('data_fields'); + $field = new xmldb_field('required', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'description'); + + // Conditionally launch add field required. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + upgrade_mod_savepoint(true, 2014051201, 'data'); + } // Moodle v2.8.0 release upgrade line. // Put any upgrade step following this. return true; } - - diff --git a/mod/data/edit.php b/mod/data/edit.php index 6dd59f75e69..7dc11db0a4d 100644 --- a/mod/data/edit.php +++ b/mod/data/edit.php @@ -34,8 +34,14 @@ $rid = optional_param('rid', 0, PARAM_INT); //record id $cancel = optional_param('cancel', '', PARAM_RAW); // cancel an add $mode ='addtemplate'; //define the mode for this page, only 1 mode available + + $url = new moodle_url('/mod/data/edit.php'); if ($rid !== 0) { + $record = $DB->get_record('data_records', array( + 'id' => $rid, + 'dataid' => $d, + ), '*', MUST_EXIST); $url->param('rid', $rid); } if ($cancel !== '') { @@ -152,105 +158,121 @@ if ($rid) { $PAGE->set_title($data->name); $PAGE->set_heading($course->fullname); -/// Process incoming data for adding/updating records +// Process incoming data for adding/updating records. +// Keep track of any notifications. +$generalnotifications = array(); +$fieldnotifications = array(); + +// Process the submitted form. if ($datarecord = data_submitted() and confirm_sesskey()) { + if ($rid) { + // Updating an existing record. - $ignorenames = array('MAX_FILE_SIZE','sesskey','d','rid','saveandview','cancel'); // strings to be ignored in input data + // Retrieve the format for the fields. + $fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d)); - if ($rid) { /// Update some records + // Validate the form to ensure that enough data was submitted. + $processeddata = data_process_submission($data, $fields, $datarecord); - /// All student edits are marked unapproved by default - $record = $DB->get_record('data_records', array('id'=>$rid)); + // Add the new notification data. + $generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications); + $fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications); - /// reset approved flag after student edit - if (!has_capability('mod/data:approve', $context)) { - $record->approved = 0; - } + if ($processeddata->validated) { + // Enough data to update the record. - $record->timemodified = time(); - $DB->update_record('data_records', $record); + // Obtain the record to be updated. - /// Update all content - $field = NULL; - foreach ($datarecord as $name => $value) { - if (!in_array($name, $ignorenames)) { - $namearr = explode('_',$name); // Second one is the field id - if (empty($field->field) || ($namearr[1] != $field->field->id)) { // Try to reuse classes - $field = data_get_field_from_id($namearr[1], $data); - } - if ($field) { - $field->update_content($rid, $value, $name); - } + // Reset the approved flag after edit if the user does not have permission to approve their own entries. + if (!has_capability('mod/data:approve', $context)) { + $record->approved = 0; } - } - // Trigger an event for updating this record. - $event = \mod_data\event\record_updated::create(array( - 'objectid' => $rid, - 'context' => $context, - 'courseid' => $course->id, - 'other' => array( - 'dataid' => $data->id - ) - )); - $event->add_record_snapshot('data', $data); - $event->trigger(); + // Update the parent record. + $record->timemodified = time(); + $DB->update_record('data_records', $record); - redirect($CFG->wwwroot.'/mod/data/view.php?d='.$data->id.'&rid='.$rid); - - } else { /// Add some new records - ///Empty form checking - you can't submit an empty form! - - $emptyform = true; // assume the worst - - foreach ($datarecord as $name => $value) { - if (!in_array($name, $ignorenames)) { - $namearr = explode('_', $name); // Second one is the field id - if (empty($field->field) || ($namearr[1] != $field->field->id)) { // Try to reuse classes - $field = data_get_field_from_id($namearr[1], $data); - } - if ($field->notemptyfield($value, $name)) { - $emptyform = false; - break; // if anything has content, this form is not empty, so stop now! - } + // Update all content. + foreach ($processeddata->fields as $fieldname => $field) { + $field->update_content($rid, $datarecord->$fieldname, $fieldname); } + + // Trigger an event for updating this record. + $event = \mod_data\event\record_updated::create(array( + 'objectid' => $rid, + 'context' => $context, + 'courseid' => $course->id, + 'other' => array( + 'dataid' => $data->id + ) + )); + $event->add_record_snapshot('data', $data); + $event->trigger(); + + $viewurl = new moodle_url('/mod/data/view.php', array( + 'd' => $data->id, + 'rid' => $rid, + )); + redirect($viewurl); } - if ($emptyform){ //nothing gets written to database - echo $OUTPUT->notification(get_string('emptyaddform','data')); - } + } else { + // No recordid was specified - creating a new entry. - if (!$emptyform && $recordid = data_add_record($data, $currentgroup)) { //add instance to data_record + // Retrieve the format for the fields. + $fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d)); - /// Insert a whole lot of empty records to make sure we have them - $fields = $DB->get_records('data_fields', array('dataid'=>$data->id)); + // Validate the form to ensure that enough data was submitted. + $processeddata = data_process_submission($data, $fields, $datarecord); + + // Add the new notification data. + $generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications); + $fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications); + + // Add instance to data_record. + if ($processeddata->validated && $recordid = data_add_record($data, $currentgroup)) { + + // Insert a whole lot of empty records to make sure we have them. + $records = array(); foreach ($fields as $field) { $content = new stdClass(); $content->recordid = $recordid; $content->fieldid = $field->id; - $DB->insert_record('data_content',$content); + $records[] = $content; } - /// For each field in the add form, add it to the data_content. - foreach ($datarecord as $name => $value){ - if (!in_array($name, $ignorenames)) { - $namearr = explode('_', $name); // Second one is the field id - if (empty($field->field) || ($namearr[1] != $field->field->id)) { // Try to reuse classes - $field = data_get_field_from_id($namearr[1], $data); - } - if ($field) { - $field->update_content($recordid, $value, $name); - } - } + // Bulk insert the records now. Some records may have no data but all must exist. + $DB->insert_records('data_content', $records); + + // Add all provided content. + foreach ($processeddata->fields as $fieldname => $field) { + $field->update_content($recordid, $datarecord->$fieldname, $fieldname); } + // Trigger an event for updating this record. + $event = \mod_data\event\record_created::create(array( + 'objectid' => $rid, + 'context' => $context, + 'courseid' => $course->id, + 'other' => array( + 'dataid' => $data->id + ) + )); + $event->add_record_snapshot('data', $data); + $event->trigger(); + if (!empty($datarecord->saveandview)) { - redirect($CFG->wwwroot.'/mod/data/view.php?d='.$data->id.'&rid='.$recordid); + $viewurl = new moodle_url('/mod/data/view.php', array( + 'd' => $data->id, + 'rid' => $recordid, + )); + redirect($viewurl); } } } -} // End of form processing +} +// End of form processing. /// Print the page header @@ -300,9 +322,18 @@ if ($data->addtemplate){ // To skip unnecessary calls to display_add_field(). if (strpos($data->addtemplate, "[[".$field->field->name."]]") !== false) { + // Replace the field tag. $patterns[] = "[[".$field->field->name."]]"; - $replacements[] = $field->display_add_field($rid); + $errors = ''; + if (!empty($fieldnotifications[$field->field->name])) { + foreach ($fieldnotifications[$field->field->name] as $notification) { + $errors .= $OUTPUT->notification($notification); + } + } + $replacements[] = $errors . $field->display_add_field($rid); } + + // Replace the field id tag. $patterns[] = "[[".$field->field->name."#id]]"; $replacements[] = 'field_'.$field->field->id; } @@ -313,6 +344,9 @@ if ($data->addtemplate){ $newtext = ''; } +foreach ($generalnotifications as $notification) { + echo $OUTPUT->notification($notification); +} echo $newtext; echo '
'; diff --git a/mod/data/field.php b/mod/data/field.php index e567fff4285..0258590034a 100644 --- a/mod/data/field.php +++ b/mod/data/field.php @@ -146,6 +146,7 @@ switch ($mode) { $field->field->name = $fieldinput->name; $field->field->description = $fieldinput->description; + $field->field->required = !empty($fieldinput->required) ? 1 : 0; for ($i=1; $i<=10; $i++) { if (isset($fieldinput->{'param'.$i})) { @@ -264,7 +265,13 @@ if (($mode == 'new') && (!empty($newtype)) && confirm_sesskey()) { /// } else { //else print quiz style list of fields $table = new html_table(); - $table->head = array(get_string('fieldname','data'), get_string('type','data'), get_string('fielddescription', 'data'), get_string('action','data')); + $table->head = array( + get_string('fieldname','data'), + get_string('type', 'data'), + get_string('required', 'data'), + get_string('fielddescription', 'data'), + get_string('action','data'), + ); $table->align = array('left','left','left', 'center'); $table->wrap = array(false,false,false,false); @@ -273,21 +280,28 @@ if (($mode == 'new') && (!empty($newtype)) && confirm_sesskey()) { /// $field = data_get_field($ff, $data); + $baseurl = new moodle_url('/mod/data/field.php', array( + 'd' => $data->id, + 'fid' => $field->field->id, + 'sesskey' => sesskey(), + )); + + $displayurl = new moodle_url($baseurl, array( + 'mode' => 'display', + )); + + $deleteurl = new moodle_url($baseurl, array( + 'mode' => 'delete', + )); + $table->data[] = array( - - ''.$field->field->name.'', - - $field->image().' '.get_string($field->type, 'data'), - - shorten_text($field->field->description, 30), - - ''. - ''.get_string('edit').''. - ' '. - ''. - ''.get_string('delete').'' - + html_writer::link($displayurl, $field->field->name), + $field->image() . ' ' . get_string($field->type, 'data'), + $field->field->required ? get_string('yes') : get_string('no'), + shorten_text($field->field->description, 30), + html_writer::link($displayurl, $OUTPUT->pix_icon('t/edit', get_string('edit'))) . + ' ' . + html_writer::link($deleteurl, $OUTPUT->pix_icon('t/delete', get_string('delete'))), ); } } diff --git a/mod/data/field/checkbox/field.class.php b/mod/data/field/checkbox/field.class.php index 7571d077a5a..500dd2181c8 100644 --- a/mod/data/field/checkbox/field.class.php +++ b/mod/data/field/checkbox/field.class.php @@ -26,19 +26,27 @@ class data_field_checkbox extends data_field_base { var $type = 'checkbox'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB; $content = array(); - if ($recordid) { + if ($formdata) { + $fieldname = 'field_' . $this->field->id; + $content = $formdata->$fieldname; + } else if ($recordid) { $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); $content = explode('##', $content); } else { $content = array(); } - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $str .= '
'.$this->field->name.''; $i = 0; @@ -211,5 +219,22 @@ class data_field_checkbox extends data_field_base { return implode('##', $vals); } -} + /** + * Check whether any boxes in the checkbox where checked. + * + * @param mixed $value The submitted values + * @param mixed $name + * @return bool + */ + function notemptyfield($value, $name) { + $found = false; + foreach ($value as $checkboxitem) { + if (!empty($checkboxitem)) { + $found = true; + break; + } + } + return $found; + } +} diff --git a/mod/data/field/checkbox/mod.html b/mod/data/field/checkbox/mod.html index a8812926b95..c6dfcaa503d 100644 --- a/mod/data/field/checkbox/mod.html +++ b/mod/data/field/checkbox/mod.html @@ -7,6 +7,10 @@ + + + field->required ? "checked=\"checked\"" : ""); ?>/> + diff --git a/mod/data/field/date/field.class.php b/mod/data/field/date/field.class.php index 2fa245d2984..9361e31e773 100644 --- a/mod/data/field/date/field.class.php +++ b/mod/data/field/date/field.class.php @@ -34,16 +34,29 @@ class data_field_date extends data_field_base { var $month = 0; var $year = 0; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $DB, $OUTPUT; - if ($recordid) { + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_day'; + $day = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id . '_month'; + $month = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id . '_year'; + $year = $formdata->$fieldname; + $content = make_timestamp($year, $month, $day, 12, 0, 0, 0, false); + } else if ($recordid) { $content = (int)$DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); } else { $content = time(); } - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $dayselector = html_writer::select_time('days', 'field_'.$this->field->id.'_day', $content); $monthselector = html_writer::select_time('months', 'field_'.$this->field->id.'_month', $content); $yearselector = html_writer::select_time('years', 'field_'.$this->field->id.'_year', $content); @@ -128,5 +141,3 @@ class data_field_date extends data_field_base { } - - diff --git a/mod/data/field/file/field.class.php b/mod/data/field/file/field.class.php index 5fd2d215d2d..567c852064e 100644 --- a/mod/data/field/file/field.class.php +++ b/mod/data/field/file/field.class.php @@ -25,7 +25,7 @@ class data_field_file extends data_field_base { var $type = 'file'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB, $OUTPUT, $PAGE, $USER; $file = false; @@ -36,7 +36,10 @@ class data_field_file extends data_field_base { $itemid = null; // editing an existing database entry - if ($recordid){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_file'; + $itemid = $formdata->$fieldname; + } else if ($recordid){ if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { file_prepare_draft_area($itemid, $this->context->id, 'mod_data', 'content', $content->id); @@ -64,7 +67,11 @@ class data_field_file extends data_field_base { $html = ''; // database entry label - $html .= '
'; + if ($this->field->required) { + $html .= '
'; + } else { + $html .= '
'; + } $html .= '
'.$this->field->name.''; // itemid element @@ -84,6 +91,9 @@ class data_field_file extends data_field_base { $output = $PAGE->get_renderer('core', 'files'); $html .= $output->render($fm); + if ($this->field->required) { + $html .= '' . get_string('requiredfieldshort', 'data') . ''; + } $html .= '
'; $html .= '
'; @@ -204,6 +214,24 @@ class data_field_file extends data_field_base { return true; } + /** + * Custom notempty function + * + * @param string $value + * @param string $name + * @return bool + */ + function notemptyfield($value, $name) { + global $USER; + + $names = explode('_', $name); + if ($names[2] == 'file') { + $usercontext = context_user::instance($USER->id); + $fs = get_file_storage(); + $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $value); + return count($files) >= 2; + } + return false; + } + } - - diff --git a/mod/data/field/file/mod.html b/mod/data/field/file/mod.html index 8f8945c5bca..2f67d9adc61 100644 --- a/mod/data/field/file/mod.html +++ b/mod/data/field/file/mod.html @@ -7,6 +7,10 @@ + + + field->required ? "checked=\"checked\"" : ""); ?>/> + diff --git a/mod/data/field/latlong/field.class.php b/mod/data/field/latlong/field.class.php index e6108eec2bb..47990c1e187 100644 --- a/mod/data/field/latlong/field.class.php +++ b/mod/data/field/latlong/field.class.php @@ -43,24 +43,41 @@ class data_field_latlong extends data_field_base { ); // Other map sources listed at http://kvaleberg.com/extensions/mapsources/index.php?params=51_30.4167_N_0_7.65_W_region:earth - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB; $lat = ''; $long = ''; - if ($recordid) { + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_0'; + $lat = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id . '_1'; + $long = $formdata->$fieldname; + } else if ($recordid) { if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { $lat = $content->content; $long = $content->content1; } } - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $str .= '
'.$this->field->name.''; $str .= ''; - $str .= ''; + $str .= ''; + if ($this->field->required) { + $str .= ''; + } + $str .= ''; $str .= '
'; $str .= '°N
°E
°E' . get_string('requiredfieldshort', 'data') . '
'; $str .= '
'; + if ($this->field->required) { + $str .= get_string('requiredfieldhint', 'data', s($this->field->description)); + } $str .= '
'; return $str; } @@ -224,5 +241,3 @@ class data_field_latlong extends data_field_base { } } - - diff --git a/mod/data/field/latlong/mod.html b/mod/data/field/latlong/mod.html index fb34fb4ed75..1b0466d9a8a 100644 --- a/mod/data/field/latlong/mod.html +++ b/mod/data/field/latlong/mod.html @@ -7,6 +7,10 @@ + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/menu/field.class.php b/mod/data/field/menu/field.class.php index 4fb611bae42..d78b4caa73b 100644 --- a/mod/data/field/menu/field.class.php +++ b/mod/data/field/menu/field.class.php @@ -26,17 +26,24 @@ class data_field_menu extends data_field_base { var $type = 'menu'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $DB, $OUTPUT; - if ($recordid){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id; + $content = $formdata->$fieldname; + } else if ($recordid){ $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); $content = trim($content); } else { $content = ''; } - - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $options = array(); $rawoptions = explode("\n",$this->field->param1); @@ -49,6 +56,9 @@ class data_field_menu extends data_field_base { $str .= html_writer::label(get_string('menuchoose', 'data'), 'field_'.$this->field->id, false, array('class' => 'accesshide')); $str .= html_writer::select($options, 'field_'.$this->field->id, $content, array(''=>get_string('menuchoose', 'data')), array('id'=>'field_'.$this->field->id)); + if ($this->field->required) { + $str .= '' . get_string('requiredfieldshort', 'data') . ''; + } $str .= '
'; @@ -108,5 +118,3 @@ class data_field_menu extends data_field_base { } } - - diff --git a/mod/data/field/menu/mod.html b/mod/data/field/menu/mod.html index f92fb136b23..62a4b018c29 100644 --- a/mod/data/field/menu/mod.html +++ b/mod/data/field/menu/mod.html @@ -7,6 +7,11 @@ + + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/multimenu/field.class.php b/mod/data/field/multimenu/field.class.php index 1f3de9ff00a..18f680d6769 100644 --- a/mod/data/field/multimenu/field.class.php +++ b/mod/data/field/multimenu/field.class.php @@ -26,10 +26,13 @@ class data_field_multimenu extends data_field_base { var $type = 'multimenu'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $DB; - if ($recordid){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id; + $content = $formdata->$fieldname; + } else if ($recordid){ $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); $content = explode('##', $content); } else { @@ -239,4 +242,3 @@ class data_field_multimenu extends data_field_base { return false; } } - diff --git a/mod/data/field/number/mod.html b/mod/data/field/number/mod.html index 831560f1b57..7b1791b0a68 100644 --- a/mod/data/field/number/mod.html +++ b/mod/data/field/number/mod.html @@ -7,4 +7,8 @@ + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/picture/field.class.php b/mod/data/field/picture/field.class.php index a459791f790..94a6e29b868 100644 --- a/mod/data/field/picture/field.class.php +++ b/mod/data/field/picture/field.class.php @@ -27,7 +27,7 @@ class data_field_picture extends data_field_base { var $previewwidth = 50; var $previewheight = 50; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB, $OUTPUT, $USER, $PAGE; $file = false; @@ -37,7 +37,10 @@ class data_field_picture extends data_field_base { $itemid = null; $fs = get_file_storage(); - if ($recordid) { + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_file'; + $itemid = $formdata->$fieldname; + } else if ($recordid) { if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { file_prepare_draft_area($itemid, $this->context->id, 'mod_data', 'content', $content->id); if (!empty($content->content)) { @@ -64,8 +67,12 @@ class data_field_picture extends data_field_base { } else { $itemid = file_get_unused_draft_itemid(); } - - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $str .= '
'.$this->field->name.''; $str .= '
'; $str .= '
'; @@ -290,6 +300,24 @@ class data_field_picture extends data_field_base { function file_ok($path) { return true; } + + /** + * Custom notempty function + * + * @param string $value + * @param string $name + * @return bool + */ + function notemptyfield($value, $name) { + global $USER; + + $names = explode('_',$name); + if ($names[2] == 'file') { + $usercontext = context_user::instance($USER->id); + $fs = get_file_storage(); + $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $value); + return count($files)>=2; + } + return false; + } } - - diff --git a/mod/data/field/picture/mod.html b/mod/data/field/picture/mod.html index 99fd424b734..47cd7ab8cee 100644 --- a/mod/data/field/picture/mod.html +++ b/mod/data/field/picture/mod.html @@ -12,6 +12,11 @@ + + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/radiobutton/field.class.php b/mod/data/field/radiobutton/field.class.php index f3115c12db8..9ae2e2893fc 100644 --- a/mod/data/field/radiobutton/field.class.php +++ b/mod/data/field/radiobutton/field.class.php @@ -26,20 +26,32 @@ class data_field_radiobutton extends data_field_base { var $type = 'radiobutton'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB; - if ($recordid){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id; + $content = $formdata->$fieldname; + } else if ($recordid){ $content = trim($DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))); } else { $content = ''; } - - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $str .= '
'.$this->field->name.''; $i = 0; - foreach (explode("\n",$this->field->param1) as $radio) { + $requiredstr = ''; + if ($this->field->required) { + $requiredstr = '' . get_string('requiredfieldshort', 'data') . ''; + } + $options = explode("\n",$this->field->param1); + foreach ($options as $radio) { $radio = trim($radio); if ($radio === '') { continue; // skip empty lines @@ -54,7 +66,11 @@ class data_field_radiobutton extends data_field_base { $str .= '/>'; } - $str .= '
'; + $str .= ''; + if ($i == count($options) - 1) { + $str .= $requiredstr; + } + $str .= '
'; $i++; } $str .= '
'; diff --git a/mod/data/field/radiobutton/mod.html b/mod/data/field/radiobutton/mod.html index a8812926b95..ebe8538f6e2 100644 --- a/mod/data/field/radiobutton/mod.html +++ b/mod/data/field/radiobutton/mod.html @@ -7,6 +7,11 @@ + + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/text/mod.html b/mod/data/field/text/mod.html index 940ff1cf3d6..fa104817053 100644 --- a/mod/data/field/text/mod.html +++ b/mod/data/field/text/mod.html @@ -7,6 +7,11 @@ + + + + field->required?"checked=\"checked\"":""); ?>/> + field->param1) {echo 'checked="checked"';} ?> value="1" /> diff --git a/mod/data/field/textarea/field.class.php b/mod/data/field/textarea/field.class.php index 51ad6fc8dd5..442e8b5bb32 100644 --- a/mod/data/field/textarea/field.class.php +++ b/mod/data/field/textarea/field.class.php @@ -49,13 +49,17 @@ class data_field_textarea extends data_field_base { return $options; } - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB, $OUTPUT, $PAGE; $text = ''; $format = 0; - - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } editors_head_setup(); $options = $this->get_options(); @@ -63,7 +67,14 @@ class data_field_textarea extends data_field_base { $itemid = $this->field->id; $field = 'field_'.$itemid; - if ($recordid && $content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_content1'; + $format = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id . '_itemid'; + $draftitemid = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id; + $text = $formdata->$fieldname; + } else if ($recordid && $content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))){ $format = $content->content1; $text = clean_text($content->content, $format); $text = file_prepare_draft_area($draftitemid, $this->context->id, 'mod_data', 'content', $content->id, $options, $text); @@ -129,8 +140,11 @@ class data_field_textarea extends data_field_base { $str .= ''; } $str .= ''; - $str .= '
'; + if ($this->field->required) { + $str .= '' . get_string('requiredfieldshort', 'data') . ''; + } + $str .= '
'; $str .= '
'; return $str; } @@ -230,5 +244,20 @@ class data_field_textarea extends data_field_base { function file_ok($relativepath) { return true; } -} + /** + * Only look at the first item (second is format) + * + * @param string $value + * @param string $name + * @return bool + */ + function notemptyfield($value, $name) { + $names = explode('_',$name); + //clean first + if (count($names) == 2) { + return !empty($value); + } + return false; + } +} diff --git a/mod/data/field/textarea/mod.html b/mod/data/field/textarea/mod.html index e75365abf9f..cf1143d2c36 100644 --- a/mod/data/field/textarea/mod.html +++ b/mod/data/field/textarea/mod.html @@ -10,6 +10,11 @@ value="field->description); ?>" /> + + + + field->required?"checked=\"checked\"":""); ?>/> + diff --git a/mod/data/field/url/field.class.php b/mod/data/field/url/field.class.php index eb0c4c57d66..7557c279bec 100644 --- a/mod/data/field/url/field.class.php +++ b/mod/data/field/url/field.class.php @@ -25,7 +25,7 @@ class data_field_url extends data_field_base { var $type = 'url'; - function display_add_field($recordid=0) { + function display_add_field($recordid = 0, $formdata = null) { global $CFG, $DB, $OUTPUT, $PAGE; require_once($CFG->dirroot. '/repository/lib.php'); // necessary for the constants used in args @@ -43,13 +43,25 @@ class data_field_url extends data_field_base { $straddlink = get_string('choosealink', 'repository'); $url = ''; $text = ''; - if ($recordid) { + if ($formdata) { + $fieldname = 'field_' . $this->field->id . '_0'; + $url = $formdata->$fieldname; + $fieldname = 'field_' . $this->field->id . '_1'; + if (isset($formdata->$fieldname)) { + $text = $formdata->$fieldname; + } + } else if ($recordid) { if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { $url = $content->content; $text = $content->content1; } } - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } if (!empty($this->field->param1) and empty($this->field->param2)) { $str .= ' + + + + diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index 5b6cc5adbe3..1e040587389 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -302,12 +302,16 @@ $string['recordsnotsaved'] = 'No entry was saved. Please check the format of the $string['recordssaved'] = 'entries saved'; $string['requireapproval'] = 'Approval required'; $string['requireapproval_help'] = 'If enabled, entries require approving by a teacher before they are viewable by everyone.'; +$string['required'] = 'Required'; $string['requiredentries'] = 'Entries required for completion'; $string['requiredentries_help'] = 'The number of entries a student is required to submit before the activity can be considered complete.'; $string['requiredentriestoview'] = 'Entries required before viewing'; $string['requiredentriestoview_help'] = 'The number of entries a student is required to submit before they can view entries from other students. Note: If entries are required before viewing, the database auto-linking filter should be disabled. This is because the database auto-linking filter can\'t determine whether a user has submitted the required number of entries.'; +$string['requiredfield'] = 'Required field'; +$string['requiredfieldhint'] = '{$a} (required field)'; +$string['requiredfieldshort'] = ' *'; $string['resetsettings'] = 'Reset filters'; $string['resettemplate'] = 'Reset template'; $string['resizingimages'] = 'Resizing image thumbnails...'; diff --git a/mod/data/lib.php b/mod/data/lib.php index 7adbedd7f3f..f3ae01f87ea 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -137,6 +137,7 @@ class data_field_base { // Base class for Database Field Types (see field/*/ $this->field->param3 = ''; $this->field->name = ''; $this->field->description = ''; + $this->field->required = false; return true; } @@ -152,6 +153,7 @@ class data_field_base { // Base class for Database Field Types (see field/*/ $this->field->name = trim($data->name); $this->field->description = trim($data->description); + $this->field->required = !empty($data->required)?1:0; if (isset($data->param1)) { $this->field->param1 = trim($data->param1); @@ -268,10 +270,13 @@ class data_field_base { // Base class for Database Field Types (see field/*/ * @param int $recordid * @return string */ - function display_add_field($recordid=0){ + function display_add_field($recordid=0, $formdata=null){ global $DB; - if ($recordid){ + if ($formdata) { + $fieldname = 'field_' . $this->field->id; + $content = $formdata->$fieldname; + } else if ($recordid){ $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); } else { $content = ''; @@ -282,9 +287,17 @@ class data_field_base { // Base class for Database Field Types (see field/*/ $content=''; } - $str = '
'; + $str = ''; + if ($this->field->required) { + $str .= '
'; + } else { + $str .= '
'; + } $str .= ''; $str .= ''; + if ($this->field->required) { + $str .= '' . get_string('requiredfieldshort', 'data') . ''; + } $str .= '
'; return $str; @@ -548,7 +561,7 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa foreach ($fields as $field) { if ($form) { // Print forms instead of data $fieldobj = data_get_field($field, $data); - $token = $fieldobj->display_add_field($recordid); + $token = $fieldobj->display_add_field($recordid, null); } else { // Just print the tag $token = '[['.$field->name.']]'; } @@ -3751,3 +3764,95 @@ function data_delete_record($recordid, $data, $courseid, $cmid) { } return false; } + +/** + * Check for required fields, and build a list of fields to be updated in a + * submission. + * + * @param $mod stdClass The current recordid - provided as an optimisation. + * @param $fields array The field data + * @param $datarecord stdClass The submitted data. + * @return stdClass containing: + * * string[] generalnotifications Notifications for the form as a whole. + * * string[] fieldnotifications Notifications for a specific field. + * * bool validated Whether the field was validated successfully. + * * data_field_base[] fields The field objects to be update. + */ +function data_process_submission(stdClass $mod, $fields, stdClass $datarecord) { + $result = new stdClass(); + + // Empty form checking - you can't submit an empty form. + $emptyform = true; + $requiredfieldsfilled = true; + + // Store the notifications. + $result->generalnotifications = array(); + $result->fieldnotifications = array(); + + // Store the instantiated classes as an optimisation when processing the result. + // This prevents the fields being re-initialised when updating. + $result->fields = array(); + + $submitteddata = array(); + foreach ($datarecord as $fieldname => $fieldvalue) { + if (strpos($fieldname, '_')) { + $namearray = explode('_', $fieldname, 3); + $fieldid = $namearray[1]; + if (!isset($submitteddata[$fieldid])) { + $submitteddata[$fieldid] = array(); + } + if (count($namearray) === 2) { + $subfieldid = 0; + } else { + $subfieldid = $namearray[2]; + } + + $fielddata = new stdClass(); + $fielddata->fieldname = $fieldname; + $fielddata->value = $fieldvalue; + $submitteddata[$fieldid][$subfieldid] = $fielddata; + } + } + + // Check all form fields which have the required are filled. + foreach ($fields as $fieldrecord) { + // Check whether the field has any data. + $fieldhascontent = false; + + $field = data_get_field($fieldrecord, $mod); + if (isset($submitteddata[$fieldrecord->id])) { + foreach ($submitteddata[$fieldrecord->id] as $fieldname => $value) { + if ($field->notemptyfield($value->value, $value->fieldname)) { + // The field has content and the form is not empty. + $fieldhascontent = true; + $emptyform = false; + } + } + } + + // If the field is required, add a notification to that effect. + if ($field->field->required && !$fieldhascontent) { + if (!isset($result->fieldnotifications[$field->field->name])) { + $result->fieldnotifications[$field->field->name] = array(); + } + $result->fieldnotifications[$field->field->name][] = get_string('required'); + $requiredfieldsfilled = false; + } + + if ($fieldhascontent) { + // The field has content so it should be updatable. + foreach ($submitteddata[$fieldrecord->id] as $value) { + $result->fields[$value->fieldname] = $field; + } + } + } + + if ($emptyform) { + // The form is empty. + $result->generalnotifications[] = get_string('emptyaddform','data'); + } + + $result->validated = $requiredfieldsfilled && !$emptyform; + + return $result; +} diff --git a/mod/data/styles.css b/mod/data/styles.css index 955b3cdc7af..1985de42d12 100644 --- a/mod/data/styles.css +++ b/mod/data/styles.css @@ -68,3 +68,14 @@ .dir-rtl .mod-data-default-template .template-field {text-align:left;} .dir-rtl .mod-data-default-template .template-token {text-align:right;} .dir-rtl .mod-data-default-template searchcontrols {text-align:left;} + +#page-mod-data-edit .privatefieldlocked { + font-style: italic; + color: #888888; +} +#page-mod-data-view .privatefieldhidden { + display: none; +} +#page-mod-data-edit .requiredfield { + color: #ff0000; +} diff --git a/mod/data/tests/behat/required_entries.feature b/mod/data/tests/behat/required_entries.feature new file mode 100644 index 00000000000..affecc4cb51 --- /dev/null +++ b/mod/data/tests/behat/required_entries.feature @@ -0,0 +1,113 @@ +@mod @mod_data +Feature: Users can be required to specify certain fields when adding entries to database activities + In order to populate databases + As a user + I need to specify certain fields when I add entries to databases + + Scenario: Students can add entries to a database + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@asd.com | + | teacher1 | Teacher | 1 | teacher1@asd.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | data | Test database name | n | C1 | data1 | + And I log in as "teacher1" + And I follow "Course 1" + And I add a "Text input" field to "Test database name" database and I fill the form with: + | Field name | Base Text input | + | Field description | Base Text input | + And I add a "Checkbox" field to "Test database name" database and I fill the form with: + | Field name | Required Checkbox | + | Field description | Required Checkbox | + | Required | yes | + | Options | Option 1 | + And I add a "Latlong" field to "Test database name" database and I fill the form with: + | Field name | Required Latlong | + | Field description | Required Latlong | + | Required | yes | + And I add a "Menu" field to "Test database name" database and I fill the form with: + | Field name | Required Menu | + | Field description | Required Menu | + | Required | yes | + | Options | Option 1 | + And I add a "Number" field to "Test database name" database and I fill the form with: + | Field name | Required Number | + | Field description | Required Number | + | Required | yes | + And I add a "Radio button" field to "Test database name" database and I fill the form with: + | Field name | Required Radio | + | Field description | Required Radio | + | Required | yes | + | Options | Option 1 | + And I add a "Text input" field to "Test database name" database and I fill the form with: + | Field name | Required Text input | + | Field description | Required Text input | + | Required | yes | + And I add a "Text area" field to "Test database name" database and I fill the form with: + | Field name | Required Text area | + | Field description | Required Text area | + | Required | yes | + And I add a "URL" field to "Test database name" database and I fill the form with: + | Field name | Required URL | + | Field description | Required URL | + | Required | yes | + And I add a "Checkbox" field to "Test database name" database and I fill the form with: + | Field name | Not required Checkbox | + | Field description | Not required Checkbox | + | Options | Option 1 | + And I add a "Latlong" field to "Test database name" database and I fill the form with: + | Field name | Not required Latlong | + | Field description | Not required Latlong | + And I add a "Menu" field to "Test database name" database and I fill the form with: + | Field name | Not required Menu | + | Field description | Not required Menu | + | Options | Option 1 | + And I add a "Number" field to "Test database name" database and I fill the form with: + | Field name | Not required Number | + | Field description | Not required Number | + And I add a "Radio button" field to "Test database name" database and I fill the form with: + | Field name | Not required Radio | + | Field description | Not required Radio | + | Options | Option 1 | + And I add a "Text input" field to "Test database name" database and I fill the form with: + | Field name | Not required Text input | + | Field description | Not required Text input | + And I add a "Text area" field to "Test database name" database and I fill the form with: + | Field name | Not required Text area | + | Field description | Not required Text area | + And I add a "URL" field to "Test database name" database and I fill the form with: + | Field name | Not required URL | + | Field description | Not required URL | + And I follow "Templates" + And I log out + When I log in as "student1" + And I follow "Course 1" + And I add an entry to "Test database name" database with: + | Base Text input | Some input to allow us to submit the for otherwise empty | + And I press "Save and view" + Then ".alert.alert-error" "css_element" should exist in the "Required Checkbox" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Latlong" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Menu" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Number" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Radio" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Text input" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required Text area" "table_row" + Then ".alert.alert-error" "css_element" should exist in the "Required URL" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Checkbox" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Latlong" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Menu" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Number" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Radio" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Text input" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required Text area" "table_row" + Then ".alert.alert-error" "css_element" should not exist in the "Not required URL" "table_row" + And I follow "View list" + And I should see "No entries in database" diff --git a/mod/data/version.php b/mod/data/version.php index 369c277660a..8df231c70a9 100644 --- a/mod/data/version.php +++ b/mod/data/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2014111000; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2014111001; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2014110400; // Requires this Moodle version $plugin->component = 'mod_data'; // Full name of the plugin (used for diagnostics) $plugin->cron = 0;
'; $str .= get_string('url','data').':'; @@ -73,6 +85,9 @@ class data_field_url extends data_field_base { $module = array('name'=>'data_urlpicker', 'fullpath'=>'/mod/data/data.js', 'requires'=>array('core_filepicker')); $PAGE->requires->js_init_call('M.data_urlpicker.init', array($options), true, $module); + if ($this->field->required) { + $str .= '' . get_string('requiredfieldshort', 'data') . ''; + } $str .= ''; return $str; } @@ -178,5 +193,3 @@ class data_field_url extends data_field_base { } } - - diff --git a/mod/data/field/url/mod.html b/mod/data/field/url/mod.html index e33b467c60f..afb5dc39026 100644 --- a/mod/data/field/url/mod.html +++ b/mod/data/field/url/mod.html @@ -11,6 +11,10 @@ field->param1) {echo 'checked="checked"';} ?> value="1" />
field->required?"checked=\"checked\"":""); ?>/>
field->param3) {echo 'checked="checked"';} ?> value="1" />