diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index 0487fbe625f..5841ddc7a05 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -69,6 +69,7 @@ $string['commentsoff'] = 'Comments feature is not enabled'; $string['configenablerssfeeds'] = 'This switch will enable the possibility of RSS feeds for all databases. You will still need to turn feeds on manually in the settings for each database.'; $string['confirmdeletefield'] = 'You are about to delete this field, are you sure?'; $string['confirmdeleterecord'] = 'Are you sure you want to delete this entry?'; +$string['confirmdeleterecords'] = 'Are you sure you want to delete these entries?'; $string['csstemplate'] = 'CSS template'; $string['csvfailed'] = 'Unable to read the raw data from the CSV file'; $string['csvfile'] = 'CSV file'; @@ -99,6 +100,7 @@ $string['dateentered'] = 'Date entered'; $string['defaultfielddelimiter'] = '(default is the comma character)'; $string['defaultfieldenclosure'] = '(default is none)'; $string['defaultsortfield'] = 'Default sort field'; +$string['delcheck'] = 'Bulk delete checkbox'; $string['delete'] = 'Delete'; $string['deleteallentries'] = 'Delete all entries'; $string['deletecomment'] = 'Are you sure you want to delete this comment?'; diff --git a/mod/data/lib.php b/mod/data/lib.php index eecd8acfd5f..8e108b9fc23 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -536,7 +536,13 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa $table->data[] = $row; } - $str = html_writer::start_tag('div', array('class' => 'defaulttemplate')); + $str = ''; + if ($template == 'listtemplate'){ + $str .= '##delcheck##'; + $str .= html_writer::empty_tag('br'); + } + + $str .= html_writer::start_tag('div', array('class' => 'defaulttemplate')); $str .= html_writer::table($table); $str .= html_writer::end_tag('div'); if ($template == 'listtemplate'){ @@ -1160,6 +1166,7 @@ function data_grade_item_delete($data) { */ function data_print_template($template, $records, $data, $search='', $page=0, $return=false) { global $CFG, $DB, $OUTPUT; + $cm = get_coursemodule_from_instance('data', $data->id); $context = context_module::instance($cm->id); @@ -1200,10 +1207,12 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r $replacement[] = highlight($search, $field->display_browse_field($record->id, $template)); } + $canmanageentries = has_capability('mod/data:manageentries', $context); + // Replacing special tags (##Edit##, ##Delete##, ##More##) $patterns[]='##edit##'; $patterns[]='##delete##'; - if (has_capability('mod/data:manageentries', $context) || (!$readonly && data_isowner($record->id))) { + if ($canmanageentries || (!$readonly && data_isowner($record->id))) { $replacement[] = ''.get_string('edit').''; $replacement[] = 'wwwroot.'/user/view.php?id='.$record->userid. '&course='.$data->course.'">'.fullname($record).''; @@ -3634,3 +3650,34 @@ function data_user_can_delete_preset($context, $preset) { return $candelete; } } + +/** + * Delete a record entry. + * + * @param int $recordid The ID for the record to be deleted. + * @param object $data The data object for this activity. + * @param int $courseid ID for the current course (for logging). + * @param int $cmid The course module ID. + * @return bool True if the record deleted, false if not. + */ +function data_delete_record($recordid, $data, $courseid, $cmid) { + global $DB; + + if ($deleterecord = $DB->get_record('data_records', array('id' => $recordid))) { + if ($deleterecord->dataid == $data->id) { + if ($contents = $DB->get_records('data_content', array('recordid' => $deleterecord->id))) { + foreach ($contents as $content) { + if ($field = data_get_field_from_id($content->fieldid, $data)) { + $field->delete_content($content->recordid); + } + } + $DB->delete_records('data_content', array('recordid'=>$deleterecord->id)); + $DB->delete_records('data_records', array('id'=>$deleterecord->id)); + + add_to_log($courseid, 'data', 'record delete', "view.php?id=$cmid", $data->id, $cmid); + return true; + } + } + } + return false; +} diff --git a/mod/data/module.js b/mod/data/module.js new file mode 100644 index 00000000000..12c981c38a6 --- /dev/null +++ b/mod/data/module.js @@ -0,0 +1,15 @@ +M.mod_data = {}; + +M.mod_data.init_view = function(Y) { + Y.on('click', function(e) { + Y.all('input.recordcheckbox').each(function() { + this.set('checked', 'checked'); + }); + }, '#checkall'); + + Y.on('click', function(e) { + Y.all('input.recordcheckbox').each(function() { + this.set('checked', ''); + }); + }, '#checknone'); +}; \ No newline at end of file diff --git a/mod/data/templates.php b/mod/data/templates.php index e7b09b66f57..4a34fb51bb9 100644 --- a/mod/data/templates.php +++ b/mod/data/templates.php @@ -253,6 +253,7 @@ if ($mode != 'csstemplate' and $mode != 'jstemplate') { // more points to single template - not useable there echo ''; echo ''; + echo ''; } echo ''; echo ''; diff --git a/mod/data/tests/lib_test.php b/mod/data/tests/lib_test.php new file mode 100644 index 00000000000..6941825a5e5 --- /dev/null +++ b/mod/data/tests/lib_test.php @@ -0,0 +1,107 @@ +. + +/** + * Unit tests for lib.php + * + * @package mod_data + * @category phpunit + * @copyright 2013 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/data/lib.php'); + +/** + * Unit tests for lib.php + * + * @package mod_data + * @copyright 2013 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class data_lib_testcase extends advanced_testcase { + + function test_data_delete_record() { + global $DB; + + $this->resetAfterTest(); + + // Create a record for deleting. + $this->setAdminUser(); + $course = $this->getDataGenerator()->create_course(); + $record = new stdClass(); + $record->course = $course->id; + $record->name = "Mod data delete test"; + $record->intro = "Some intro of some sort"; + + $module = $this->getDataGenerator()->create_module('data', $record); + + $field = data_get_field_new('text', $module); + + $fielddetail = new stdClass(); + $fielddetail->d = $module->id; + $fielddetail->mode = 'add'; + $fielddetail->type = 'text'; + $fielddetail->sesskey = sesskey(); + $fielddetail->name = 'Name'; + $fielddetail->description = 'Some name'; + + $field->define_field($fielddetail); + $field->insert_field(); + $recordid = data_add_record($module); + + $datacontent = array(); + $datacontent['fieldid'] = $field->field->id; + $datacontent['recordid'] = $recordid; + $datacontent['content'] = 'Asterix'; + + $contentid = $DB->insert_record('data_content', $datacontent); + $cm = get_coursemodule_from_instance('data', $module->id, $course->id); + + // Check to make sure that we have a database record. + $data = $DB->get_records('data', array('id' => $module->id)); + $this->assertEquals(1, count($data)); + + $datacontent = $DB->get_records('data_content', array('id' => $contentid)); + $this->assertEquals(1, count($datacontent)); + + $datafields = $DB->get_records('data_fields', array('id' => $field->field->id)); + $this->assertEquals(1, count($datafields)); + + $datarecords = $DB->get_records('data_records', array('id' => $recordid)); + $this->assertEquals(1, count($datarecords)); + + // Test to see if a failed delete returns false. + $result = data_delete_record(8798, $module, $course->id, $cm->id); + $this->assertFalse($result); + + // Delete the record. + $result = data_delete_record($recordid, $module, $course->id, $cm->id); + + // Check that all of the record is gone. + $datacontent = $DB->get_records('data_content', array('id' => $contentid)); + $this->assertEquals(0, count($datacontent)); + + $datarecords = $DB->get_records('data_records', array('id' => $recordid)); + $this->assertEquals(0, count($datarecords)); + + // Make sure the function returns true on a successful deletion. + $this->assertTrue($result); + } +} \ No newline at end of file diff --git a/mod/data/view.php b/mod/data/view.php index 2437350f7ec..7812c267e59 100644 --- a/mod/data/view.php +++ b/mod/data/view.php @@ -40,6 +40,8 @@ /// These can be added to perform an action on a record $approve = optional_param('approve', 0, PARAM_INT); //approval recordid $delete = optional_param('delete', 0, PARAM_INT); //delete recordid + $multidelete = optional_param_array('delcheck', null, PARAM_INT); + $serialdelete = optional_param('serialdelete', null, PARAM_RAW); if ($id) { if (! $cm = get_coursemodule_from_id('data', $id)) { @@ -326,16 +328,17 @@ groups_print_activity_menu($cm, $returnurl); $currentgroup = groups_get_activity_group($cm); $groupmode = groups_get_activity_groupmode($cm); + $canmanageentries = has_capability('mod/data:manageentries', $context); // If a student is not part of a group and seperate groups is enabled, we don't // want them seeing all records. - if ($currentgroup == 0 && $groupmode == 1 && !has_capability('mod/data:manageentries', $context)) { + if ($currentgroup == 0 && $groupmode == 1 && !$canmanageentries) { $canviewallrecords = false; } else { $canviewallrecords = true; } // detect entries not approved yet and show hint instead of not found error - if ($record and $data->approval and !$record->approved and $record->userid != $USER->id and !has_capability('mod/data:manageentries', $context)) { + if ($record and $data->approval and !$record->approved and $record->userid != $USER->id and !$canmanageentries) { if (!$currentgroup or $record->groupid == $currentgroup or $record->groupid == 0) { print_error('notapproved', 'data'); } @@ -360,26 +363,11 @@ /// Delete any requested records - if ($delete && confirm_sesskey() && (has_capability('mod/data:manageentries', $context) or data_isowner($delete))) { + if ($delete && confirm_sesskey() && ($canmanageentries or data_isowner($delete))) { if ($confirm = optional_param('confirm',0,PARAM_INT)) { - if ($deleterecord = $DB->get_record('data_records', array('id'=>$delete))) { // Need to check this is valid - if ($deleterecord->dataid == $data->id) { // Must be from this database - if ($contents = $DB->get_records('data_content', array('recordid'=>$deleterecord->id))) { - foreach ($contents as $content) { // Delete files or whatever else this field allows - if ($field = data_get_field_from_id($content->fieldid, $data)) { // Might not be there - $field->delete_content($content->recordid); - } - } - } - $DB->delete_records('data_content', array('recordid'=>$deleterecord->id)); - $DB->delete_records('data_records', array('id'=>$deleterecord->id)); - - add_to_log($course->id, 'data', 'record delete', "view.php?id=$cm->id", $data->id, $cm->id); - - echo $OUTPUT->notification(get_string('recorddeleted','data'), 'notifysuccess'); - } + if (data_delete_record($delete, $data, $course->id, $cm->id)) { + echo $OUTPUT->notification(get_string('recorddeleted','data'), 'notifysuccess'); } - } else { // Print a confirmation page if ($deleterecord = $DB->get_record('data_records', array('id'=>$delete))) { // Need to check this is valid if ($deleterecord->dataid == $data->id) { // Must be from this database @@ -398,9 +386,44 @@ } + // Multi-delete. + if ($serialdelete) { + $multidelete = json_decode($serialdelete); + } + + if ($multidelete && confirm_sesskey() && $canmanageentries) { + if ($confirm = optional_param('confirm', 0, PARAM_INT)) { + foreach ($multidelete as $value) { + data_delete_record($value, $data, $course->id, $cm->id); + } + } else { + $validrecords = array(); + $recordids = array(); + foreach ($multidelete as $value) { + if ($deleterecord = $DB->get_record('data_records', array('id'=>$value))) { // Need to check this is valid + if ($deleterecord->dataid == $data->id) { // Must be from this database + $validrecords[] = $deleterecord; + $recordids[] = $deleterecord->id; + } + } + } + + $serialiseddata = json_encode($recordids); + $submitactions = array('d' => $data->id, 'sesskey' => sesskey(), 'confirm' => '1', 'serialdelete' => $serialiseddata); + $action = new moodle_url('/mod/data/view.php', $submitactions); + $cancelurl = new moodle_url('/mod/data/view.php', array('d' => $data->id)); + $deletebutton = new single_button($action, get_string('delete')); + echo $OUTPUT->confirm(get_string('confirmdeleterecords', 'data'), $deletebutton, $cancelurl); + echo data_print_template('listtemplate', $validrecords, $data, '', 0, false); + echo $OUTPUT->footer(); + exit; + } + } + + //if data activity closed dont let students in $showactivity = true; -if (!has_capability('mod/data:manageentries', $context)) { +if (!$canmanageentries) { $timenow = time(); if (!empty($data->timeavailablefrom) && $data->timeavailablefrom > $timenow) { echo $OUTPUT->notification(get_string('notopenyet', 'data', userdate($data->timeavailablefrom))); @@ -423,6 +446,9 @@ if ($showactivity) { } include('tabs.php'); + $url = new moodle_url('/mod/data/view.php', array('d' => $data->id, 'sesskey' => sesskey())); + echo html_writer::start_tag('form', array('action' => $url, 'method' => 'post')); + if ($mode == 'asearch') { $maxcount = 0; @@ -446,7 +472,7 @@ if ($showactivity) { $numentries = data_numentries($data); /// Check the number of entries required against the number of entries already made (doesn't apply to teachers) - if ($data->requiredentries > 0 && $numentries < $data->requiredentries && !has_capability('mod/data:manageentries', $context)) { + if ($data->requiredentries > 0 && $numentries < $data->requiredentries && !$canmanageentries) { $data->entriesleft = $data->requiredentries - $numentries; $strentrieslefttoadd = get_string('entrieslefttoadd', 'data', $data); echo $OUTPUT->notification($strentrieslefttoadd); @@ -454,7 +480,7 @@ if ($showactivity) { /// Check the number of entries required before to view other participant's entries against the number of entries already made (doesn't apply to teachers) $requiredentries_allowed = true; - if ($data->requiredentriestoview > 0 && $numentries < $data->requiredentriestoview && !has_capability('mod/data:manageentries', $context)) { + if ($data->requiredentriestoview > 0 && $numentries < $data->requiredentriestoview && !$canmanageentries) { $data->entrieslefttoview = $data->requiredentriestoview - $numentries; $strentrieslefttoaddtoview = get_string('entrieslefttoaddtoview', 'data', $data); echo $OUTPUT->notification($strentrieslefttoaddtoview); @@ -665,7 +691,7 @@ if ($showactivity) { if (!$records = $DB->get_records_sql($sqlselect, $allparams, $page * $nowperpage, $nowperpage)) { // Nothing to show! if ($record) { // Something was requested so try to show that at least (bug 5132) - if (has_capability('mod/data:manageentries', $context) || empty($data->approval) || + if ($canmanageentries || empty($data->approval) || $record->approved || (isloggedin() && $record->userid == $USER->id)) { if (!$currentgroup || $record->groupid == $currentgroup || $record->groupid == 0) { // OK, we can show this one @@ -776,8 +802,20 @@ if ($showactivity) { echo $button->to_html(PORTFOLIO_ADD_FULL_FORM); } + //Advanced search form doesn't make sense for single (redirects list view) if (($maxcount || $mode == 'asearch') && $mode != 'single') { + if ($canmanageentries) { + echo html_writer::start_tag('div', array('class' => 'form-buttons')); + echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checkall', 'value' => get_string('selectall'))); + echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checknone', 'value' => get_string('deselectall'))); + echo html_writer::empty_tag('input', array('class' => 'form-submit', 'type' => 'submit', 'value' => get_string('deleteselected'))); + echo html_writer::end_tag('div'); + + $module = array('name'=>'mod_data', 'fullpath'=>'/mod/data/module.js'); + $PAGE->requires->js_init_call('M.mod_data.init_view', null, false, $module); + } + echo html_writer::end_tag('form'); data_print_preference_form($data, $perpage, $search, $sort, $order, $search_array, $advanced, $mode); } }