mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 00:42:54 +02:00
Merge branch 'MDL-57455_master' of git://github.com/markn86/moodle
This commit is contained in:
commit
6b76e041f4
@ -47,6 +47,9 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
|
||||
'assessed', 'assesstimestart', 'assesstimefinish', 'defaultsort',
|
||||
'defaultsortdir', 'editany', 'notification', 'timemodified', 'config', 'completionentries'));
|
||||
|
||||
$tags = new backup_nested_element('recordstags');
|
||||
$tag = new backup_nested_element('tag', array('id'), array('itemid', 'rawname'));
|
||||
|
||||
$fields = new backup_nested_element('fields');
|
||||
|
||||
$field = new backup_nested_element('field', array('id'), array(
|
||||
@ -84,6 +87,9 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
|
||||
$record->add_child($ratings);
|
||||
$ratings->add_child($rating);
|
||||
|
||||
$data->add_child($tags);
|
||||
$tags->add_child($tag);
|
||||
|
||||
// Define sources
|
||||
$data->set_source_table('data', array('id' => backup::VAR_ACTIVITYID));
|
||||
|
||||
@ -104,6 +110,18 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
|
||||
'component' => backup_helper::is_sqlparam('mod_data'),
|
||||
'ratingarea' => backup_helper::is_sqlparam('entry')));
|
||||
$rating->set_source_alias('rating', 'value');
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$tag->set_source_sql('SELECT t.id, ti.itemid, t.rawname
|
||||
FROM {tag} t
|
||||
JOIN {tag_instance} ti
|
||||
ON ti.tagid = t.id
|
||||
WHERE ti.itemtype = ?
|
||||
AND ti.component = ?
|
||||
AND ti.contextid = ?', array(
|
||||
backup_helper::is_sqlparam('data_records'),
|
||||
backup_helper::is_sqlparam('mod_data'),
|
||||
backup::VAR_CONTEXTID));
|
||||
}
|
||||
}
|
||||
|
||||
// Define id annotations
|
||||
|
@ -42,6 +42,7 @@ class restore_data_activity_structure_step extends restore_activity_structure_st
|
||||
$paths[] = new restore_path_element('data_record', '/activity/data/records/record');
|
||||
$paths[] = new restore_path_element('data_content', '/activity/data/records/record/contents/content');
|
||||
$paths[] = new restore_path_element('data_rating', '/activity/data/records/record/ratings/rating');
|
||||
$paths[] = new restore_path_element('data_record_tag', '/activity/data/recordstags/tag');
|
||||
}
|
||||
|
||||
// Return the paths wrapped into standard activity structure
|
||||
@ -121,6 +122,28 @@ class restore_data_activity_structure_step extends restore_activity_structure_st
|
||||
$this->set_mapping('data_content', $oldid, $newitemid, true); // files by this itemname
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tags to restored records.
|
||||
*
|
||||
* @param stdClass $data Tag
|
||||
*/
|
||||
protected function process_data_record_tag($data) {
|
||||
$data = (object)$data;
|
||||
|
||||
if (!core_tag_tag::is_enabled('mod_data', 'data_records')) { // Tags disabled in server, nothing to process.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$itemid = $this->get_mappingid('data_record', $data->itemid)) {
|
||||
// Some orphaned tag, we could not find the data record for it - ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
$tag = $data->rawname;
|
||||
$context = context_module::instance($this->task->get_moduleid());
|
||||
core_tag_tag::add_item_tag('mod_data', 'data_records', $itemid, $context, $tag);
|
||||
}
|
||||
|
||||
protected function process_data_rating($data) {
|
||||
global $DB;
|
||||
|
||||
|
34
mod/data/db/tag.php
Normal file
34
mod/data/db/tag.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tag areas in component mod_data
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2017 Andrew Hancox <andrewdchancox@googlemail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$tagareas = array(
|
||||
array(
|
||||
'itemtype' => 'data_records',
|
||||
'component' => 'mod_data',
|
||||
'callback' => 'mod_data_get_tagged_records',
|
||||
'callbackfile' => '/mod/data/locallib.php',
|
||||
),
|
||||
);
|
@ -33,8 +33,7 @@ $d = optional_param('d', 0, PARAM_INT); // database id
|
||||
$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
|
||||
|
||||
|
||||
$tags = optional_param_array('tags', [], PARAM_TAGLIST);
|
||||
|
||||
$url = new moodle_url('/mod/data/edit.php');
|
||||
if ($rid !== 0) {
|
||||
@ -182,6 +181,7 @@ if ($datarecord = data_submitted() and confirm_sesskey()) {
|
||||
if ($processeddata->validated) {
|
||||
// Enough data to update the record.
|
||||
data_update_record_fields_contents($data, $record, $context, $datarecord, $processeddata);
|
||||
core_tag_tag::set_item_tags('mod_data', 'data_records', $rid, $context, $tags);
|
||||
|
||||
$viewurl = new moodle_url('/mod/data/view.php', array(
|
||||
'd' => $data->id,
|
||||
@ -209,6 +209,8 @@ if ($datarecord = data_submitted() and confirm_sesskey()) {
|
||||
// Now populate the fields contents of the new record.
|
||||
data_add_fields_contents_to_new_record($data, $context, $recordid, $fields, $datarecord, $processeddata);
|
||||
|
||||
core_tag_tag::set_item_tags('mod_data', 'data_records', $recordid, $context, $tags);
|
||||
|
||||
if (!empty($datarecord->saveandview)) {
|
||||
$viewurl = new moodle_url('/mod/data/view.php', array(
|
||||
'd' => $data->id,
|
||||
@ -287,6 +289,12 @@ if ($data->addtemplate){
|
||||
$patterns[] = "[[".$field->field->name."#id]]";
|
||||
$replacements[] = 'field_'.$field->field->id;
|
||||
}
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$patterns[] = "##tags##";
|
||||
$replacements[] = data_generate_tag_form($rid);
|
||||
}
|
||||
|
||||
$newtext = str_ireplace($patterns, $replacements, $data->{$mode});
|
||||
|
||||
} else { //if the add template is not yet defined, print the default form!
|
||||
|
@ -32,6 +32,7 @@ $d = required_param('d', PARAM_INT);
|
||||
$exportuser = optional_param('exportuser', false, PARAM_BOOL); // Flag for exporting user details
|
||||
$exporttime = optional_param('exporttime', false, PARAM_BOOL); // Flag for exporting date/time information
|
||||
$exportapproval = optional_param('exportapproval', false, PARAM_BOOL); // Flag for exporting user details
|
||||
$tags = optional_param('exporttags', false, PARAM_BOOL); // Flag for exporting user details.
|
||||
|
||||
$PAGE->set_url('/mod/data/export.php', array('d'=>$d));
|
||||
|
||||
@ -111,7 +112,7 @@ foreach ($formdata as $key => $value) {
|
||||
$currentgroup = groups_get_activity_group($cm);
|
||||
|
||||
$exportdata = data_get_exportdata($data->id, $fields, $selectedfields, $currentgroup, $context,
|
||||
$exportuser, $exporttime, $exportapproval);
|
||||
$exportuser, $exporttime, $exportapproval, $tags);
|
||||
$count = count($exportdata);
|
||||
switch ($formdata['exporttype']) {
|
||||
case 'csv':
|
||||
|
@ -85,6 +85,11 @@ class mod_data_export_form extends moodleform {
|
||||
if ($this->_data->approval) {
|
||||
$mform->addElement('checkbox', 'exportapproval', get_string('includeapproval', 'data'));
|
||||
}
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$mform->addElement('checkbox', 'exporttags', get_string('includetags', 'data'));
|
||||
}
|
||||
|
||||
$this->add_action_buttons(true, get_string('exportentries', 'data'));
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ if (!$formdata = $form->get_data()) {
|
||||
$errorfield = '';
|
||||
$safetoskipfields = array(get_string('user'), get_string('username'), get_string('email'),
|
||||
get_string('timeadded', 'data'), get_string('timemodified', 'data'),
|
||||
get_string('approved', 'data'));
|
||||
get_string('approved', 'data'), get_string('tags', 'data'));
|
||||
foreach ($fieldnames as $name => $id) {
|
||||
if (!isset($rawfields[$name])) {
|
||||
if (!in_array($name, $safetoskipfields)) {
|
||||
@ -156,6 +156,21 @@ if (!$formdata = $form->get_data()) {
|
||||
$DB->insert_record('data_content', $content);
|
||||
}
|
||||
}
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records') &&
|
||||
isset($fieldnames[get_string('tags', 'data')])) {
|
||||
$columnindex = $fieldnames[get_string('tags', 'data')];
|
||||
$rawtags = $record[$columnindex];
|
||||
$tags = explode(',', $rawtags);
|
||||
foreach ($tags as $tag) {
|
||||
$tag = trim($tag);
|
||||
if (empty($tag)) {
|
||||
continue;
|
||||
}
|
||||
core_tag_tag::add_item_tag('mod_data', 'data_records', $recordid, $context, $tag);
|
||||
}
|
||||
}
|
||||
|
||||
$recordsadded++;
|
||||
print get_string('added', 'moodle', $recordsadded) . ". " . get_string('entry', 'data') . " (ID $recordid)<br />\n";
|
||||
}
|
||||
|
@ -201,6 +201,7 @@ $string['expired'] = 'Sorry, this activity closed on {$a} and is no longer avail
|
||||
$string['importentries'] = 'Import entries';
|
||||
$string['importsuccess'] = 'The preset has been successfully applied.';
|
||||
$string['includeapproval'] = 'Include approval status';
|
||||
$string['includetags'] = 'Include tags';
|
||||
$string['includetime'] = 'Include time added/modified';
|
||||
$string['includeuserdetails'] = 'Include user details';
|
||||
$string['indicator:cognitivedepth'] = 'Database cognitive';
|
||||
@ -305,6 +306,7 @@ $string['recorddeleted'] = 'Entry deleted';
|
||||
$string['recorddisapproved'] = 'Entry unapproved';
|
||||
$string['recordsnotsaved'] = 'No entry was saved. Please check the format of the uploaded file.';
|
||||
$string['recordssaved'] = 'entries saved';
|
||||
$string['removealldatatags'] = 'Remove all database tags';
|
||||
$string['requireapproval'] = 'Approval required';
|
||||
$string['requireapproval_help'] = 'If enabled, entries require approving by a teacher before they are viewable by everyone.';
|
||||
$string['required'] = 'Required';
|
||||
@ -345,6 +347,9 @@ $string['subplugintype_datafield'] = 'Database field type';
|
||||
$string['subplugintype_datafield_plural'] = 'Database field types';
|
||||
$string['subplugintype_datapreset'] = 'Preset';
|
||||
$string['subplugintype_datapreset_plural'] = 'Presets';
|
||||
$string['tagarea_data_records'] = 'Data records';
|
||||
$string['tags'] = 'tags';
|
||||
$string['tagsdeleted'] = 'Database tags have been deleted';
|
||||
$string['teachersandstudents'] = '{$a->teachers} and {$a->students}';
|
||||
$string['templates'] = 'Templates';
|
||||
$string['templatesaved'] = 'Template saved';
|
||||
|
163
mod/data/lib.php
163
mod/data/lib.php
@ -32,6 +32,7 @@ define ('DATA_LASTNAME', -2);
|
||||
define ('DATA_APPROVED', -3);
|
||||
define ('DATA_TIMEADDED', 0);
|
||||
define ('DATA_TIMEMODIFIED', -4);
|
||||
define ('DATA_TAGS', -5);
|
||||
|
||||
define ('DATA_CAP_EXPORT', 'mod/data:viewalluserpresets');
|
||||
|
||||
@ -622,6 +623,17 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa
|
||||
$token
|
||||
);
|
||||
}
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$label = new html_table_cell(get_string('tags') . ':');
|
||||
if ($form) {
|
||||
$cell = data_generate_tag_form();
|
||||
} else {
|
||||
$cell = new html_table_cell('##tags##');
|
||||
}
|
||||
$table->data[] = new html_table_row(array($label, $cell));
|
||||
}
|
||||
|
||||
if ($template == 'listtemplate') {
|
||||
$cell = new html_table_cell('##edit## ##more## ##delete## ##approve## ##disapprove## ##export##');
|
||||
$cell->colspan = 2;
|
||||
@ -666,6 +678,75 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the form elements to manage tags for a record.
|
||||
*
|
||||
* @param int|bool $recordid
|
||||
* @param string[] $selected raw tag names
|
||||
* @return string
|
||||
*/
|
||||
function data_generate_tag_form($recordid = false, $selected = []) {
|
||||
global $CFG, $DB, $OUTPUT, $PAGE;
|
||||
|
||||
$tagtypestoshow = \core_tag_area::get_showstandard('mod_data', 'data_records');
|
||||
$showstandard = ($tagtypestoshow != core_tag_tag::HIDE_STANDARD);
|
||||
$typenewtags = ($tagtypestoshow != core_tag_tag::STANDARD_ONLY);
|
||||
|
||||
$str = html_writer::start_tag('div', array('class' => 'datatagcontrol'));
|
||||
|
||||
$namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
|
||||
|
||||
$tagcollid = \core_tag_area::get_collection('mod_data', 'data_records');
|
||||
$tags = [];
|
||||
$selectedtags = [];
|
||||
|
||||
if ($showstandard) {
|
||||
$tags += $DB->get_records_menu('tag', array('isstandard' => 1, 'tagcollid' => $tagcollid),
|
||||
$namefield, 'id,' . $namefield . ' as fieldname');
|
||||
}
|
||||
|
||||
if ($recordid) {
|
||||
$selectedtags += core_tag_tag::get_item_tags_array('mod_data', 'data_records', $recordid);
|
||||
}
|
||||
|
||||
if (!empty($selected)) {
|
||||
list($sql, $params) = $DB->get_in_or_equal($selected, SQL_PARAMS_NAMED);
|
||||
$params['tagcollid'] = $tagcollid;
|
||||
$sql = "SELECT id, $namefield FROM {tag} WHERE tagcollid = :tagcollid AND rawname $sql";
|
||||
$selectedtags += $DB->get_records_sql_menu($sql, $params);
|
||||
}
|
||||
|
||||
$tags += $selectedtags;
|
||||
|
||||
$str .= '<select class="custom-select" name="tags[]" id="tags" multiple>';
|
||||
foreach ($tags as $tagid => $tag) {
|
||||
$selected = key_exists($tagid, $selectedtags) ? 'selected' : '';
|
||||
$str .= "<option value='$tag' $selected>$tag</option>";
|
||||
}
|
||||
$str .= '</select>';
|
||||
|
||||
if (has_capability('moodle/tag:manage', context_system::instance()) && $showstandard) {
|
||||
$url = new moodle_url('/tag/manage.php', array('tc' => core_tag_area::get_collection('mod_data',
|
||||
'data_records')));
|
||||
$str .= ' ' . $OUTPUT->action_link($url, get_string('managestandardtags', 'tag'));
|
||||
}
|
||||
|
||||
$PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array(
|
||||
'#tags',
|
||||
$typenewtags,
|
||||
'',
|
||||
get_string('entertags', 'tag'),
|
||||
false,
|
||||
$showstandard,
|
||||
get_string('noselection', 'form')
|
||||
)
|
||||
);
|
||||
|
||||
$str .= html_writer::end_tag('div');
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search for a field name and replaces it with another one in all the
|
||||
@ -1442,6 +1523,12 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r
|
||||
$replacement[] = '';
|
||||
}
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$patterns[] = "##tags##";
|
||||
$replacement[] = $OUTPUT->tag_list(
|
||||
core_tag_tag::get_item_tags('mod_data', 'data_records', $record->id), '', 'data-tags');
|
||||
}
|
||||
|
||||
// actual replacement of the tags
|
||||
$newtext = str_ireplace($patterns, $replacement, $data->{$template});
|
||||
|
||||
@ -1824,6 +1911,12 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
|
||||
$replacement[] = '<label class="accesshide" for="u_ln">' . get_string('authorlastname', 'data') . '</label>' .
|
||||
'<input type="text" class="form-control" size="16" id="u_ln" name="u_ln" value="' . s($ln) . '" />';
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$patterns[] = "/##tags##/";
|
||||
$selectedtags = isset($search_array[DATA_TAGS]->rawtagnames) ? $search_array[DATA_TAGS]->rawtagnames : [];
|
||||
$replacement[] = data_generate_tag_form(false, $selectedtags);
|
||||
}
|
||||
|
||||
// actual replacement of the tags
|
||||
$newtext = preg_replace($patterns, $replacement, $data->asearchtemplate);
|
||||
|
||||
@ -2707,6 +2800,9 @@ function data_reset_course_form_definition(&$mform) {
|
||||
|
||||
$mform->addElement('checkbox', 'reset_data_comments', get_string('deleteallcomments'));
|
||||
$mform->disabledIf('reset_data_comments', 'reset_data', 'checked');
|
||||
|
||||
$mform->addElement('checkbox', 'reset_data_tags', get_string('removealldatatags', 'data'));
|
||||
$mform->disabledIf('reset_data_tags', 'reset_data', 'checked');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2791,6 +2887,8 @@ function data_reset_userdata($data) {
|
||||
|
||||
$ratingdeloptions->contextid = $datacontext->id;
|
||||
$rm->delete_ratings($ratingdeloptions);
|
||||
|
||||
core_tag_tag::delete_instances('mod_data', null, $datacontext->id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2833,6 +2931,8 @@ function data_reset_userdata($data) {
|
||||
}
|
||||
$notenrolled[$record->userid] = true;
|
||||
|
||||
core_tag_tag::remove_all_item_tags('mod_data', 'data_records', $record->id);
|
||||
|
||||
$DB->delete_records('comments', array('itemid' => $record->id, 'commentarea' => 'database_entry'));
|
||||
$DB->delete_records('data_content', array('recordid' => $record->id));
|
||||
$DB->delete_records('data_records', array('id' => $record->id));
|
||||
@ -2870,6 +2970,22 @@ function data_reset_userdata($data) {
|
||||
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false);
|
||||
}
|
||||
|
||||
// Remove all the tags.
|
||||
if (!empty($data->reset_data_tags)) {
|
||||
if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
|
||||
foreach ($datas as $dataid => $unused) {
|
||||
if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$context = context_module::instance($cm->id);
|
||||
core_tag_tag::delete_instances('mod_data', null, $context->id);
|
||||
|
||||
}
|
||||
}
|
||||
$status[] = array('component' => $componentstr, 'item' => get_string('tagsdeleted', 'data'), 'error' => false);
|
||||
}
|
||||
|
||||
// updating dates - shift may be negative too
|
||||
if ($data->timeshift) {
|
||||
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
||||
@ -3016,10 +3132,11 @@ function data_export_ods($export, $dataname, $count) {
|
||||
* @param bool $userdetails whether to include the details of the record author
|
||||
* @param bool $time whether to include time created/modified
|
||||
* @param bool $approval whether to include approval status
|
||||
* @param bool $tags whether to include tags
|
||||
* @return array
|
||||
*/
|
||||
function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null,
|
||||
$userdetails=false, $time=false, $approval=false) {
|
||||
$userdetails=false, $time=false, $approval=false, $tags = false) {
|
||||
global $DB;
|
||||
|
||||
if (is_null($context)) {
|
||||
@ -3051,6 +3168,9 @@ function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0,
|
||||
if ($approval) {
|
||||
$exportdata[0][] = get_string('approved', 'data');
|
||||
}
|
||||
if ($tags) {
|
||||
$exportdata[0][] = get_string('tags', 'data');
|
||||
}
|
||||
|
||||
$datarecords = $DB->get_records('data_records', array('dataid'=>$dataid));
|
||||
ksort($datarecords);
|
||||
@ -3086,6 +3206,10 @@ function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0,
|
||||
if ($approval) { // Add approval status
|
||||
$exportdata[$line][] = (int) $record->approved;
|
||||
}
|
||||
if ($tags) {
|
||||
$itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id);
|
||||
$exportdata[$line][] = implode(', ', $itemtags);
|
||||
}
|
||||
}
|
||||
$line++;
|
||||
}
|
||||
@ -3783,14 +3907,14 @@ function data_get_recordids($alias, $searcharray, $dataid, $recordids) {
|
||||
}
|
||||
list($insql, $params) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED);
|
||||
$nestselect = 'SELECT c' . $alias . '.recordid
|
||||
FROM {data_content} c' . $alias . ',
|
||||
{data_fields} f,
|
||||
{data_records} r,
|
||||
{user} u ';
|
||||
$nestwhere = 'WHERE u.id = r.userid
|
||||
AND f.id = c' . $alias . '.fieldid
|
||||
AND r.id = c' . $alias . '.recordid
|
||||
AND r.dataid = :dataid
|
||||
FROM {data_content} c' . $alias . '
|
||||
INNER JOIN {data_fields} f
|
||||
ON f.id = c' . $alias . '.fieldid
|
||||
INNER JOIN {data_records} r
|
||||
ON r.id = c' . $alias . '.recordid
|
||||
INNER JOIN {user} u
|
||||
ON u.id = r.userid ';
|
||||
$nestwhere = 'WHERE r.dataid = :dataid
|
||||
AND c' . $alias .'.recordid ' . $insql . '
|
||||
AND ';
|
||||
|
||||
@ -3801,6 +3925,25 @@ function data_get_recordids($alias, $searcharray, $dataid, $recordids) {
|
||||
} else if ($searchcriteria == DATA_TIMEMODIFIED) {
|
||||
$nestsql = $nestselect . $nestwhere . $nestsearch->field . ' >= :timemodified GROUP BY c' . $alias . '.recordid';
|
||||
$params['timemodified'] = $nestsearch->data;
|
||||
} else if ($searchcriteria == DATA_TAGS) {
|
||||
if (empty($nestsearch->rawtagnames)) {
|
||||
return [];
|
||||
}
|
||||
$i = 0;
|
||||
$tagwhere = [];
|
||||
$tagselect = '';
|
||||
foreach ($nestsearch->rawtagnames as $tagrawname) {
|
||||
$tagselect .= " INNER JOIN {tag_instance} AS ti_$i
|
||||
ON ti_$i.component = 'mod_data'
|
||||
AND ti_$i.itemtype = 'data_records'
|
||||
AND ti_$i.itemid = r.id
|
||||
INNER JOIN {tag} AS t_$i
|
||||
ON ti_$i.tagid = t_$i.id ";
|
||||
$tagwhere[] = " t_$i.rawname = :trawname_$i ";
|
||||
$params["trawname_$i"] = $tagrawname;
|
||||
$i++;
|
||||
}
|
||||
$nestsql = $nestselect . $tagselect . $nestwhere . implode(' AND ', $tagwhere);
|
||||
} else { // First name or last name.
|
||||
$thing = $DB->sql_like($nestsearch->field, ':search1', false);
|
||||
$nestsql = $nestselect . $nestwhere . $thing . ' GROUP BY c' . $alias . '.recordid';
|
||||
@ -3947,6 +4090,8 @@ function data_delete_record($recordid, $data, $courseid, $cmid) {
|
||||
data_rss_delete_file($data);
|
||||
}
|
||||
|
||||
core_tag_tag::remove_all_item_tags('mod_data', 'data_records', $recordid);
|
||||
|
||||
// Trigger an event for deleting this record.
|
||||
$event = \mod_data\event\record_deleted::create(array(
|
||||
'objectid' => $deleterecord->id,
|
||||
|
@ -771,6 +771,250 @@ function data_get_entries_left_to_view($data, $numentries, $canmanageentries) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data records tagged with a specified tag.
|
||||
*
|
||||
* This is a callback used by the tag area mod_data/data_records to search for data records
|
||||
* tagged with a specific tag.
|
||||
*
|
||||
* @param core_tag_tag $tag
|
||||
* @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
|
||||
* are displayed on the page and the per-page limit may be bigger
|
||||
* @param int $fromctx context id where the link was displayed, may be used by callbacks
|
||||
* to display items in the same context first
|
||||
* @param int $ctx context id where to search for records
|
||||
* @param bool $rec search in subcontexts as well
|
||||
* @param int $page 0-based number of page being displayed
|
||||
* @return \core_tag\output\tagindex
|
||||
*/
|
||||
function mod_data_get_tagged_records($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = true, $page = 0) {
|
||||
global $DB, $OUTPUT, $USER;
|
||||
$perpage = $exclusivemode ? 20 : 5;
|
||||
|
||||
// Build the SQL query.
|
||||
$ctxselect = context_helper::get_preload_record_columns_sql('ctx');
|
||||
$query = "SELECT dr.id, dr.dataid, dr.approved, d.timeviewfrom, d.timeviewto, dr.groupid, d.approval, dr.userid,
|
||||
d.requiredentriestoview, cm.id AS cmid, c.id AS courseid, c.shortname, c.fullname, $ctxselect
|
||||
FROM {data_records} dr
|
||||
JOIN {data} d
|
||||
ON d.id = dr.dataid
|
||||
JOIN {modules} m
|
||||
ON m.name = 'data'
|
||||
JOIN {course_modules} cm
|
||||
ON cm.module = m.id AND cm.instance = d.id
|
||||
JOIN {tag_instance} tt
|
||||
ON dr.id = tt.itemid
|
||||
JOIN {course} c
|
||||
ON cm.course = c.id
|
||||
JOIN {context} ctx
|
||||
ON ctx.instanceid = cm.id AND ctx.contextlevel = :coursemodulecontextlevel
|
||||
WHERE tt.itemtype = :itemtype
|
||||
AND tt.tagid = :tagid
|
||||
AND tt.component = :component
|
||||
AND cm.deletioninprogress = 0
|
||||
AND dr.id %ITEMFILTER%
|
||||
AND c.id %COURSEFILTER%";
|
||||
|
||||
$params = array(
|
||||
'itemtype' => 'data_records',
|
||||
'tagid' => $tag->id,
|
||||
'component' => 'mod_data',
|
||||
'coursemodulecontextlevel' => CONTEXT_MODULE
|
||||
);
|
||||
|
||||
if ($ctx) {
|
||||
$context = $ctx ? context::instance_by_id($ctx) : context_system::instance();
|
||||
$query .= $rec ? ' AND (ctx.id = :contextid OR ctx.path LIKE :path)' : ' AND ctx.id = :contextid';
|
||||
$params['contextid'] = $context->id;
|
||||
$params['path'] = $context->path . '/%';
|
||||
}
|
||||
|
||||
$query .= " ORDER BY ";
|
||||
if ($fromctx) {
|
||||
// In order-clause specify that modules from inside "fromctx" context should be returned first.
|
||||
$fromcontext = context::instance_by_id($fromctx);
|
||||
$query .= ' (CASE WHEN ctx.id = :fromcontextid OR ctx.path LIKE :frompath THEN 0 ELSE 1 END),';
|
||||
$params['fromcontextid'] = $fromcontext->id;
|
||||
$params['frompath'] = $fromcontext->path . '/%';
|
||||
}
|
||||
$query .= ' c.sortorder, cm.id, dr.id';
|
||||
|
||||
$totalpages = $page + 1;
|
||||
|
||||
// Use core_tag_index_builder to build and filter the list of items.
|
||||
$builder = new core_tag_index_builder('mod_data', 'data_records', $query, $params, $page * $perpage, $perpage + 1);
|
||||
$now = time();
|
||||
$entrycount = [];
|
||||
$activitygroupmode = [];
|
||||
$usergroups = [];
|
||||
$titlefields = [];
|
||||
while ($item = $builder->has_item_that_needs_access_check()) {
|
||||
context_helper::preload_from_record($item);
|
||||
$modinfo = get_fast_modinfo($item->courseid);
|
||||
$cm = $modinfo->get_cm($item->cmid);
|
||||
$context = \context_module::instance($cm->id);
|
||||
$courseid = $item->courseid;
|
||||
|
||||
if (!$builder->can_access_course($courseid)) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$cm->uservisible) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!has_capability('mod/data:viewentry', $context)) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($USER->id != $item->userid && (($item->timeviewfrom && $now < $item->timeviewfrom)
|
||||
|| ($item->timeviewto && $now > $item->timeviewto))) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($USER->id != $item->userid && $item->approval && !$item->approved) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($item->requiredentriestoview) {
|
||||
if (!isset($entrycount[$item->dataid])) {
|
||||
$entrycount[$item->dataid] = $DB->count_records('data_records', array('dataid' => $item->dataid));
|
||||
}
|
||||
$sufficiententries = $item->requiredentriestoview > $entrycount[$item->dataid];
|
||||
$builder->set_accessible($item, $sufficiententries);
|
||||
}
|
||||
|
||||
if (!isset($activitygroupmode[$cm->id])) {
|
||||
$activitygroupmode[$cm->id] = groups_get_activity_groupmode($cm);
|
||||
}
|
||||
|
||||
if (!isset($usergroups[$item->groupid])) {
|
||||
$usergroups[$item->groupid] = groups_is_member($item->groupid, $USER->id);
|
||||
}
|
||||
|
||||
if ($activitygroupmode[$cm->id] == SEPARATEGROUPS && !$usergroups[$item->groupid]) {
|
||||
$builder->set_accessible($item, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
$builder->set_accessible($item, true);
|
||||
}
|
||||
|
||||
$items = $builder->get_items();
|
||||
if (count($items) > $perpage) {
|
||||
$totalpages = $page + 2; // We don't need exact page count, just indicate that the next page exists.
|
||||
array_pop($items);
|
||||
}
|
||||
|
||||
// Build the display contents.
|
||||
if ($items) {
|
||||
$tagfeed = new core_tag\output\tagfeed();
|
||||
foreach ($items as $item) {
|
||||
context_helper::preload_from_record($item);
|
||||
$modinfo = get_fast_modinfo($item->courseid);
|
||||
$cm = $modinfo->get_cm($item->cmid);
|
||||
$pageurl = new moodle_url('/mod/data/view.php', array(
|
||||
'rid' => $item->id,
|
||||
'd' => $item->dataid
|
||||
));
|
||||
|
||||
if (!isset($titlefields[$item->dataid])) {
|
||||
$titlefields[$item->dataid] = data_get_tag_title_field($item->dataid);
|
||||
}
|
||||
|
||||
$pagename = data_get_tag_title_for_entry($titlefields[$item->dataid], $item);
|
||||
$pagename = html_writer::link($pageurl, $pagename);
|
||||
$courseurl = course_get_url($item->courseid, $cm->sectionnum);
|
||||
$cmname = html_writer::link($cm->url, $cm->get_formatted_name());
|
||||
$coursename = format_string($item->fullname, true, array('context' => context_course::instance($item->courseid)));
|
||||
$coursename = html_writer::link($courseurl, $coursename);
|
||||
$icon = html_writer::link($pageurl, html_writer::empty_tag('img', array('src' => $cm->get_icon_url())));
|
||||
$tagfeed->add($icon, $pagename, $cmname . '<br>' . $coursename);
|
||||
}
|
||||
$content = $OUTPUT->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($OUTPUT));
|
||||
|
||||
return new core_tag\output\tagindex($tag, 'mod_data', 'data_records', $content, $exclusivemode,
|
||||
$fromctx, $ctx, $rec, $page, $totalpages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of a field to show when displaying tag results.
|
||||
*
|
||||
* @param int $dataid The id of the data field
|
||||
* @return stdClass The field data from the 'data_fields' table as well as it's priority
|
||||
*/
|
||||
function data_get_tag_title_field($dataid) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url');
|
||||
$fields = $DB->get_records('data_fields', ['dataid' => $dataid]);
|
||||
$template = $DB->get_field('data', 'addtemplate', ['id' => $dataid]);
|
||||
|
||||
$filteredfields = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (!in_array($field->type, $validfieldtypes)) {
|
||||
continue;
|
||||
}
|
||||
$field->addtemplateposition = strpos($template, '[['.$field->name.']]');
|
||||
if ($field->addtemplateposition === false) {
|
||||
continue;
|
||||
}
|
||||
require_once($CFG->dirroot . '/mod/data/field/' . $field->type . '/field.class.php');
|
||||
$classname = 'data_field_' . $field->type;
|
||||
$field->priority = $classname::get_priority();
|
||||
$filteredfields[] = $field;
|
||||
}
|
||||
|
||||
$sort = function($record1, $record2) {
|
||||
// If a content's fieldtype is compulsory in the database than it would have priority than any other non-compulsory content.
|
||||
if (($record1->required && $record2->required) || (!$record1->required && !$record2->required)) {
|
||||
if ($record1->priority === $record2->priority) {
|
||||
return $record1->id < $record2->id ? 1 : -1;
|
||||
}
|
||||
|
||||
return $record1->priority < $record2->priority ? -1 : 1;
|
||||
} else if ($record1->required && !$record2->required) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
usort($filteredfields, $sort);
|
||||
|
||||
return array_shift($filteredfields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of an entry to show when displaying tag results.
|
||||
*
|
||||
* @param stdClass $field The field from the 'data_fields' table
|
||||
* @param stdClass $entry The entry from the 'data_records' table
|
||||
* @return string The title of the entry
|
||||
*/
|
||||
function data_get_tag_title_for_entry($field, $entry) {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->dirroot . '/mod/data/field/' . $field->type . '/field.class.php');
|
||||
|
||||
$classname = 'data_field_' . $field->type;
|
||||
$sql = "SELECT dc.*
|
||||
FROM {data_content} dc
|
||||
INNER JOIN {data_fields} df
|
||||
ON dc.fieldid = df.id
|
||||
WHERE df.id = :fieldid
|
||||
AND dc.recordid = :recordid";
|
||||
$fieldcontents = $DB->get_record_sql($sql, array('recordid' => $entry->id, 'fieldid' => $field->id));
|
||||
|
||||
return $classname::get_content_value($fieldcontents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search entries in a database.
|
||||
*
|
||||
@ -908,26 +1152,7 @@ function data_search_entries($data, $cm, $context, $mode, $currentgroup, $search
|
||||
$initialparams['myid3'] = $params['myid2'];
|
||||
}
|
||||
|
||||
if (!empty($advanced)) { // If advanced box is checked.
|
||||
$i = 0;
|
||||
foreach ($searcharray as $key => $val) { // what does $searcharray hold?
|
||||
if ($key == DATA_FIRSTNAME or $key == DATA_LASTNAME) {
|
||||
$i++;
|
||||
$searchselect .= " AND ".$DB->sql_like($val->field, ":search_flname_$i", false);
|
||||
$params['search_flname_'.$i] = "%$val->data%";
|
||||
continue;
|
||||
}
|
||||
if ($key == DATA_TIMEMODIFIED) {
|
||||
$searchselect .= " AND $val->field >= :timemodified";
|
||||
$params['timemodified'] = $val->data;
|
||||
continue;
|
||||
}
|
||||
$advtables .= ', {data_content} c'.$key.' ';
|
||||
$advwhere .= ' AND c'.$key.'.recordid = r.id';
|
||||
$advsearchselect .= ' AND ('.$val->sql.') ';
|
||||
$advparams = array_merge($advparams, $val->params);
|
||||
}
|
||||
} else if ($search) {
|
||||
if ($search) {
|
||||
$searchselect = " AND (".$DB->sql_like('c.content', ':search1', false)."
|
||||
OR ".$DB->sql_like('u.firstname', ':search2', false)."
|
||||
OR ".$DB->sql_like('u.lastname', ':search3', false)." ) ";
|
||||
@ -965,26 +1190,8 @@ function data_search_entries($data, $cm, $context, $mode, $currentgroup, $search
|
||||
$params['myid2'] = $USER->id;
|
||||
$initialparams['myid3'] = $params['myid2'];
|
||||
}
|
||||
$i = 0;
|
||||
if (!empty($advanced)) { // If advanced box is checked.
|
||||
foreach ($searcharray as $key => $val) { // what does $searcharray hold?
|
||||
if ($key == DATA_FIRSTNAME or $key == DATA_LASTNAME) {
|
||||
$i++;
|
||||
$searchselect .= " AND ".$DB->sql_like($val->field, ":search_flname_$i", false);
|
||||
$params['search_flname_'.$i] = "%$val->data%";
|
||||
continue;
|
||||
}
|
||||
if ($key == DATA_TIMEMODIFIED) {
|
||||
$searchselect .= " AND $val->field >= :timemodified";
|
||||
$params['timemodified'] = $val->data;
|
||||
continue;
|
||||
}
|
||||
$advtables .= ', {data_content} c'.$key.' ';
|
||||
$advwhere .= ' AND c'.$key.'.recordid = r.id AND c'.$key.'.fieldid = '.$key;
|
||||
$advsearchselect .= ' AND ('.$val->sql.') ';
|
||||
$advparams = array_merge($advparams, $val->params);
|
||||
}
|
||||
} else if ($search) {
|
||||
|
||||
if ($search) {
|
||||
$searchselect = " AND (".$DB->sql_like('c.content', ':search1', false)." OR
|
||||
".$DB->sql_like('u.firstname', ':search2', false)." OR
|
||||
".$DB->sql_like('u.lastname', ':search3', false)." ) ";
|
||||
@ -1146,6 +1353,17 @@ function data_build_search_array($data, $paging, $searcharray, $defaults = null,
|
||||
}
|
||||
}
|
||||
|
||||
$rawtagnames = optional_param_array('tags', false, PARAM_TAGLIST);
|
||||
|
||||
if ($rawtagnames) {
|
||||
$searcharray[DATA_TAGS] = new stdClass();
|
||||
$searcharray[DATA_TAGS]->params = [];
|
||||
$searcharray[DATA_TAGS]->rawtagnames = $rawtagnames;
|
||||
$searcharray[DATA_TAGS]->sql = '';
|
||||
} else {
|
||||
unset($searcharray[DATA_TAGS]);
|
||||
}
|
||||
|
||||
if (!$paging) {
|
||||
// Name searching.
|
||||
$fn = optional_param('u_fn', $fn, PARAM_NOTAGS);
|
||||
|
@ -146,3 +146,7 @@
|
||||
#page-mod-data-edit .mod-data-input {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#page-mod-data-edit .datatagcontrol {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
@ -259,6 +259,11 @@ if ($mode != 'csstemplate' and $mode != 'jstemplate') {
|
||||
echo '<option value="[['.$field->name.'#id]]" title="'.$field->description.' id">'.$field->name.' id - [['.$field->name.'#id]]</option>';
|
||||
}
|
||||
echo '</optgroup>';
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
echo '<optgroup label="'.get_string('other', 'data').'">';
|
||||
echo '<option value="##tags##">' . get_string('tags') . ' - ##tags##</option>';
|
||||
echo '</optgroup>';
|
||||
}
|
||||
}
|
||||
|
||||
// Print special tags. fix for MDL-7031
|
||||
@ -284,6 +289,11 @@ if ($mode != 'csstemplate' and $mode != 'jstemplate') {
|
||||
echo '<option value="##user##">' .get_string('user'). ' - ##user##</option>';
|
||||
echo '<option value="##userpicture##">' . get_string('userpic') . ' - ##userpicture##</option>';
|
||||
echo '<option value="##approvalstatus##">' .get_string('approvalstatus', 'data'). ' - ##approvalstatus##</option>';
|
||||
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
echo '<option value="##tags##">' . get_string('tags') . ' - ##tags##</option>';
|
||||
}
|
||||
|
||||
if ($mode != 'singletemplate') {
|
||||
// more points to single template - not useable there
|
||||
echo '<option value="##comments##">' .get_string('comments', 'data'). ' - ##comments##</option>';
|
||||
|
@ -4,68 +4,134 @@ Feature: Users can view and search database entries
|
||||
As a user
|
||||
I need to list and search the database entries
|
||||
|
||||
Scenario: Students can add view, list and search entries
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Bob | 1 | student1@example.com |
|
||||
| student2 | Alice | 2 | student2@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "tags" exist:
|
||||
| name | isstandard |
|
||||
| Tag1 | 1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| data | Test database name | n | C1 | data1 |
|
||||
| activity | name | intro | course | idnumber |
|
||||
| data | Test database name | Database intro | C1 | data1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I add a "Text input" field to "Test database name" database and I fill the form with:
|
||||
| Field name | Test field name |
|
||||
| Field name | Test field name |
|
||||
| Field description | Test field description |
|
||||
And I add a "Text input" field to "Test database name" database and I fill the form with:
|
||||
| Field name | Test field 2 name |
|
||||
| Field description | Test field 2 description |
|
||||
# To generate the default templates.
|
||||
And I follow "Templates"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Teacher entry 1 |
|
||||
And I press "Save and add another"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Teacher entry 2 |
|
||||
And I press "Save and add another"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Teacher entry 3 |
|
||||
And I press "Save and view"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
|
||||
Scenario: Students can add view, list and search entries
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test database name"
|
||||
Then I should see "Teacher entry 1"
|
||||
And I should see "Teacher entry 2"
|
||||
And I should see "Teacher entry 3"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student entry 1 |
|
||||
And I press "Save and add another"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student entry 2 |
|
||||
And I press "Save and add another"
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student entry 3 |
|
||||
And I press "Save and view"
|
||||
And I follow "Test database name"
|
||||
Then I should see "Student entry 1"
|
||||
And I should see "Student entry 2"
|
||||
And I should see "Student entry 3"
|
||||
And I follow "View single"
|
||||
And I should see "Teacher entry 1"
|
||||
And I should not see "Teacher entry 2"
|
||||
And I should see "Student entry 1"
|
||||
And I should not see "Student entry 2"
|
||||
And "2" "link" should exist
|
||||
And "3" "link" should exist
|
||||
And I follow "Next"
|
||||
And I should see "Teacher entry 2"
|
||||
And I should not see "Teacher entry 1"
|
||||
And I should see "Student entry 2"
|
||||
And I should not see "Student entry 1"
|
||||
And I click on "3" "link" in the "region-main" "region"
|
||||
And I should see "Teacher entry 3"
|
||||
And I should not see "Teacher entry 2"
|
||||
And I should see "Student entry 3"
|
||||
And I should not see "Student entry 2"
|
||||
And I follow "Previous"
|
||||
And I should see "Teacher entry 2"
|
||||
And I should not see "Teacher entry 1"
|
||||
And I should not see "Teacher entry 3"
|
||||
And I should see "Student entry 2"
|
||||
And I should not see "Student entry 1"
|
||||
And I should not see "Student entry 3"
|
||||
And I follow "Search"
|
||||
And I set the field "Test field name" to "Teacher entry 1"
|
||||
And I set the field "Test field name" to "Student entry 1"
|
||||
And I press "Save settings"
|
||||
And I should see "Teacher entry 1"
|
||||
And I should not see "Teacher entry 2"
|
||||
And I should not see "Teacher entry 3"
|
||||
And I should see "Student entry 1"
|
||||
And I should not see "Student entry 2"
|
||||
And I should not see "Student entry 3"
|
||||
And I follow "Search"
|
||||
And I set the field "Test field name" to "Teacher entry"
|
||||
And I set the field "Test field name" to "Student entry"
|
||||
And I set the field "Order" to "Descending"
|
||||
And I press "Save settings"
|
||||
And "Teacher entry 3" "text" should appear before "Teacher entry 2" "text"
|
||||
And "Teacher entry 2" "text" should appear before "Teacher entry 1" "text"
|
||||
And "Student entry 3" "text" should appear before "Student entry 2" "text"
|
||||
And "Student entry 2" "text" should appear before "Student entry 1" "text"
|
||||
|
||||
@javascript
|
||||
Scenario: Check that searching by tags works as expected
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student original entry untagged |
|
||||
| Test field 2 name | Student original entry untagged 2 |
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student original entry tagged |
|
||||
| Test field 2 name | Student original entry tagged 2 |
|
||||
And I set the field with xpath "//div[@class='datatagcontrol']//input[@type='text']" to "Tag1"
|
||||
And I click on "[data-value='Tag1']" "css_element"
|
||||
And I press "Save and view"
|
||||
And I should see "Student original entry"
|
||||
And I should see "Tag1" in the "div.tag_list" "css_element"
|
||||
And I follow "Edit"
|
||||
And I should see "Tag1" in the ".form-autocomplete-selection" "css_element"
|
||||
And I follow "View list"
|
||||
And I should see "Tag1" in the "div.tag_list" "css_element"
|
||||
And I follow "Search"
|
||||
And I set the field with xpath "//div[@class='datatagcontrol']//input[@type='text']" to "Tag1"
|
||||
And I click on "[data-value='Tag1']" "css_element"
|
||||
When I press "Save settings"
|
||||
Then I should see "Student original entry tagged"
|
||||
And I should see "Student original entry tagged 2"
|
||||
And I should not see "Student original entry untagged"
|
||||
And I should not see "Student original entry untagged 2"
|
||||
|
||||
Scenario: Check that searching by first and last name works as expected
|
||||
Given I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student entry 1 |
|
||||
And I press "Save and view"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I am on "Course 1" course homepage
|
||||
And I add an entry to "Test database name" database with:
|
||||
| Test field name | Student entry 2 |
|
||||
And I press "Save and view"
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test database name"
|
||||
And I follow "Search"
|
||||
And I set the field "Author first name" to "Bob"
|
||||
And I press "Save settings"
|
||||
Then I should see "Student entry 1"
|
||||
And I should not see "Student entry 2"
|
||||
And I set the field "Author first name" to ""
|
||||
And I set the field "Author surname" to "2"
|
||||
And I press "Save settings"
|
||||
And I should not see "Student entry 1"
|
||||
And I should see "Student entry 2"
|
||||
|
@ -209,15 +209,24 @@ class mod_data_generator extends testing_module_generator {
|
||||
*
|
||||
* @param mod_data $data
|
||||
* @param array $contents
|
||||
* @param int $groupid
|
||||
* @param array $tags
|
||||
* @param array $options
|
||||
* @return data_field_{type}
|
||||
*/
|
||||
public function create_entry($data, array $contents, $groupid = 0) {
|
||||
public function create_entry($data, array $contents, $groupid = 0, $tags = [], array $options = null) {
|
||||
global $DB;
|
||||
|
||||
$this->databaserecordcount++;
|
||||
|
||||
$recordid = data_add_record($data, $groupid);
|
||||
|
||||
if (isset($options['approved'])) {
|
||||
data_approve_entry($recordid, !empty($options['approved']));
|
||||
} else {
|
||||
$approved = null;
|
||||
}
|
||||
|
||||
$fields = $DB->get_records('data_fields', array('dataid' => $data->id));
|
||||
|
||||
// Validating whether required field are filled.
|
||||
@ -309,6 +318,12 @@ class mod_data_generator extends testing_module_generator {
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($tags)) {
|
||||
$cm = get_coursemodule_from_instance('data', $data->id);
|
||||
core_tag_tag::set_item_tags('mod_data', 'data_records', $recordid,
|
||||
context_module::instance($cm->id), $tags);
|
||||
}
|
||||
|
||||
return $recordid;
|
||||
}
|
||||
}
|
||||
|
@ -198,8 +198,10 @@ class mod_data_generator_testcase extends advanced_testcase {
|
||||
$fieldcontents[$fieldrecord->id] = $contents[$count++];
|
||||
}
|
||||
|
||||
$datarecordid = $this->getDataGenerator()->get_plugin_generator('mod_data')->create_entry($data, $fieldcontents,
|
||||
$groupa->id);
|
||||
$tags = ['Cats', 'mice'];
|
||||
|
||||
$datarecordid = $this->getDataGenerator()->get_plugin_generator('mod_data')->create_entry($data,
|
||||
$fieldcontents, $groupa->id, $tags);
|
||||
|
||||
$this->assertEquals(1, $DB->count_records('data_records', array('dataid' => $data->id)));
|
||||
$this->assertEquals(count($contents), $DB->count_records('data_content', array('recordid' => $datarecordid)));
|
||||
@ -229,5 +231,7 @@ class mod_data_generator_testcase extends advanced_testcase {
|
||||
$this->assertEquals($contents[$contentstartid]->content1, '1');
|
||||
$this->assertEquals($contents[++$contentstartid]->content, 'http://example.url');
|
||||
$this->assertEquals($contents[$contentstartid]->content1, 'sampleurl');
|
||||
$this->assertEquals(array('Cats', 'mice'),
|
||||
array_values(core_tag_tag::get_item_tags_array('mod_data', 'data_records', $datarecordid)));
|
||||
}
|
||||
}
|
||||
|
@ -976,6 +976,271 @@ class mod_data_lib_testcase extends advanced_testcase {
|
||||
$this->assertEquals(1, $completiondata->completionstate);
|
||||
}
|
||||
|
||||
public function test_mod_data_get_tagged_records() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
|
||||
$datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
|
||||
$datagenerator->create_entry($data1, [$field1->field->id => 'value12'], 0, ['Cats', 'mice']);
|
||||
$datagenerator->create_entry($data1, [$field1->field->id => 'value13'], 0, ['Cats']);
|
||||
$datagenerator->create_entry($data1, [$field1->field->id => 'value14'], 0);
|
||||
|
||||
$tag = core_tag_tag::get_by_name(0, 'Cats');
|
||||
|
||||
// Admin can see everything.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value12', $res->content);
|
||||
$this->assertContains('value13', $res->content);
|
||||
$this->assertNotContains('value14', $res->content);
|
||||
}
|
||||
|
||||
public function test_mod_data_get_tagged_records_approval() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
$data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id, 'approval' => true));
|
||||
$field2 = $datagenerator->create_field($fieldrecord, $data2);
|
||||
|
||||
$record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
|
||||
$record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats'], ['approved' => false]);
|
||||
$tag = core_tag_tag::get_by_name(0, 'Cats');
|
||||
|
||||
// Admin can see everything.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertEmpty($res->prevpageurl);
|
||||
$this->assertEmpty($res->nextpageurl);
|
||||
|
||||
// Create and enrol a user.
|
||||
$student = self::getDataGenerator()->create_user();
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
|
||||
$this->setUser($student);
|
||||
|
||||
// User can search data records inside a course.
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertNotContains('value21', $res->content);
|
||||
|
||||
$recordtoupdate = new stdClass();
|
||||
$recordtoupdate->id = $record21;
|
||||
$recordtoupdate->approved = true;
|
||||
$DB->update_record('data_records', $recordtoupdate);
|
||||
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
}
|
||||
|
||||
public function test_mod_data_get_tagged_records_time() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
|
||||
$timefrom = time() - YEARSECS;
|
||||
$timeto = time() - WEEKSECS;
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
$data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id,
|
||||
'timeviewfrom' => $timefrom,
|
||||
'timeviewto' => $timeto));
|
||||
$field2 = $datagenerator->create_field($fieldrecord, $data2);
|
||||
$record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
|
||||
$record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
|
||||
$tag = core_tag_tag::get_by_name(0, 'Cats');
|
||||
|
||||
// Admin can see everything.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertEmpty($res->prevpageurl);
|
||||
$this->assertEmpty($res->nextpageurl);
|
||||
|
||||
// Create and enrol a user.
|
||||
$student = self::getDataGenerator()->create_user();
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
|
||||
$this->setUser($student);
|
||||
|
||||
// User can search data records inside a course.
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertNotContains('value21', $res->content);
|
||||
|
||||
$data2->timeviewto = time() + YEARSECS;
|
||||
$DB->update_record('data', $data2);
|
||||
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
}
|
||||
|
||||
public function test_mod_data_get_tagged_records_course_enrolment() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
$data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
|
||||
$field2 = $datagenerator->create_field($fieldrecord, $data2);
|
||||
|
||||
$record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
|
||||
$record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
|
||||
$tag = core_tag_tag::get_by_name(0, 'Cats');
|
||||
|
||||
// Admin can see everything.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertEmpty($res->prevpageurl);
|
||||
$this->assertEmpty($res->nextpageurl);
|
||||
|
||||
// Create and enrol a user.
|
||||
$student = self::getDataGenerator()->create_user();
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
|
||||
$this->setUser($student);
|
||||
core_tag_index_builder::reset_caches();
|
||||
|
||||
// User can search data records inside a course.
|
||||
$coursecontext = context_course::instance($course1->id);
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertNotContains('value21', $res->content);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
|
||||
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
}
|
||||
|
||||
public function test_mod_data_get_tagged_records_course_groups() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$groupa = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupA'));
|
||||
$groupb = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupB'));
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
$data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
|
||||
$field2 = $datagenerator->create_field($fieldrecord, $data2);
|
||||
set_coursemodule_groupmode($data2->cmid, SEPARATEGROUPS);
|
||||
|
||||
$record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'],
|
||||
0, ['Cats', 'Dogs']);
|
||||
$record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'],
|
||||
$groupa->id, ['Cats']);
|
||||
$record22 = $datagenerator->create_entry($data2, [$field2->field->id => 'value22'],
|
||||
$groupb->id, ['Cats']);
|
||||
$tag = core_tag_tag::get_by_name(0, 'Cats');
|
||||
|
||||
// Admin can see everything.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertContains('value22', $res->content);
|
||||
$this->assertEmpty($res->prevpageurl);
|
||||
$this->assertEmpty($res->nextpageurl);
|
||||
|
||||
// Create and enrol a user.
|
||||
$student = self::getDataGenerator()->create_user();
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
|
||||
groups_add_member($groupa, $student);
|
||||
$this->setUser($student);
|
||||
core_tag_index_builder::reset_caches();
|
||||
|
||||
// User can search data records inside a course.
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertNotContains('value22', $res->content);
|
||||
|
||||
groups_add_member($groupb, $student);
|
||||
core_tag_index_builder::reset_caches();
|
||||
$res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
|
||||
|
||||
$this->assertContains('value11', $res->content);
|
||||
$this->assertContains('value21', $res->content);
|
||||
$this->assertContains('value22', $res->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test check_updates_since callback.
|
||||
*/
|
||||
|
@ -257,6 +257,40 @@ class mod_data_search_test extends advanced_testcase {
|
||||
$this->assertEquals($this->approvedatarecordcount, count($recordids));
|
||||
}
|
||||
|
||||
public function test_advanced_search_tags() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Setup test data.
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fieldrecord = new StdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
$fieldrecord->titlefield = true;
|
||||
|
||||
$data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
|
||||
$field1 = $datagenerator->create_field($fieldrecord, $data1);
|
||||
|
||||
$record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
|
||||
$record12 = $datagenerator->create_entry($data1, [$field1->field->id => 'value12'], 0, ['Cats', 'mice']);
|
||||
$record13 = $datagenerator->create_entry($data1, [$field1->field->id => 'value13'], 0, ['Bats']);
|
||||
|
||||
$searcharray = [];
|
||||
$searcharray[DATA_TAGS] = new stdClass();
|
||||
$searcharray[DATA_TAGS]->params = [];
|
||||
$searcharray[DATA_TAGS]->rawtagnames = ['Cats'];
|
||||
$searcharray[DATA_TAGS]->sql = '';
|
||||
|
||||
$recordids = data_get_all_recordids($data1->id);
|
||||
$newrecordids = data_get_advance_search_ids($recordids, $searcharray, $data1->id);
|
||||
|
||||
$this->assertContains($record11, $newrecordids);
|
||||
$this->assertContains($record12, $newrecordids);
|
||||
$this->assertNotContains($record13, $newrecordids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indexing database entries contents.
|
||||
*
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2017051500; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2017091400; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2017050500; // Requires this Moodle version
|
||||
$plugin->component = 'mod_data'; // Full name of the plugin (used for diagnostics)
|
||||
$plugin->cron = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user