MDL-57455 mod_data: Implement tagging
[moodle.git] / mod / data / lib.php
CommitLineData
4636bf83 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
3d4b223a 17
4636bf83 18/**
002a2a44 19 * @package mod_data
4636bf83 20 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23
c4b70cfc
SB
24defined('MOODLE_INTERNAL') || die();
25
b8b554ac 26// Some constants
0997e51a 27define ('DATA_MAX_ENTRIES', 50);
28define ('DATA_PERPAGE_SINGLE', 1);
b572ce19 29
714bec74 30define ('DATA_FIRSTNAME', -1);
31define ('DATA_LASTNAME', -2);
bb5740f4 32define ('DATA_APPROVED', -3);
3239b010 33define ('DATA_TIMEADDED', 0);
34define ('DATA_TIMEMODIFIED', -4);
714bec74 35
a7e35395 36define ('DATA_CAP_EXPORT', 'mod/data:viewalluserpresets');
8aff1574
SH
37
38define('DATA_PRESET_COMPONENT', 'mod_data');
39define('DATA_PRESET_FILEAREA', 'site_presets');
40define('DATA_PRESET_CONTEXT', SYSCONTEXTID);
41
7c6f961b
MN
42define('DATA_EVENT_TYPE_OPEN', 'open');
43define('DATA_EVENT_TYPE_CLOSE', 'close');
44
a7e35395 45// Users having assigned the default role "Non-editing teacher" can export database records
46// Using the mod/data capability "viewalluserpresets" existing in Moodle 1.9.x.
8429163d 47// In Moodle >= 2, new roles may be introduced and used instead.
a7e35395 48
4636bf83 49/**
002a2a44 50 * @package mod_data
4636bf83 51 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
52 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
53 */
668fc89a 54class data_field_base { // Base class for Database Field Types (see field/*/field.class.php)
3d4b223a 55
4636bf83 56 /** @var string Subclasses must override the type with their name */
57 var $type = 'unknown';
58 /** @var object The database object that this field belongs to */
59 var $data = NULL;
60 /** @var object The field object itself, if we know it */
56c1ca88 61 var $field = NULL;
4636bf83 62 /** @var int Width of the icon for this fieldtype */
63 var $iconwidth = 16;
64 /** @var int Width of the icon for this fieldtype */
65 var $iconheight = 16;
66 /** @var object course module or cmifno */
67 var $cm;
68 /** @var object activity context */
69 var $context;
2af9ad7d
DG
70 /** @var priority for globalsearch indexing */
71 protected static $priority = self::NO_PRIORITY;
72 /** priority value for invalid fields regarding indexing */
73 const NO_PRIORITY = 0;
74 /** priority value for minimum priority */
75 const MIN_PRIORITY = 1;
76 /** priority value for low priority */
77 const LOW_PRIORITY = 2;
78 /** priority value for high priority */
79 const HIGH_PRIORITY = 3;
80 /** priority value for maximum priority */
81 const MAX_PRIORITY = 4;
4636bf83 82
83 /**
84 * Constructor function
85 *
86 * @global object
87 * @uses CONTEXT_MODULE
88 * @param int $field
89 * @param int $data
90 * @param int $cm
91 */
8429163d 92 function __construct($field=0, $data=0, $cm=0) { // Field or data or both, each can be id or object
9c00b5d7 93 global $DB;
0997e51a 94
95 if (empty($field) && empty($data)) {
29c1bb15 96 print_error('missingfield', 'data');
0997e51a 97 }
b572ce19 98
0997e51a 99 if (!empty($field)) {
100 if (is_object($field)) {
101 $this->field = $field; // Programmer knows what they are doing, we hope
9c00b5d7 102 } else if (!$this->field = $DB->get_record('data_fields', array('id'=>$field))) {
29c1bb15 103 print_error('invalidfieldid', 'data');
0997e51a 104 }
105 if (empty($data)) {
9c00b5d7 106 if (!$this->data = $DB->get_record('data', array('id'=>$this->field->dataid))) {
29c1bb15 107 print_error('invalidid', 'data');
0997e51a 108 }
109 }
110 }
b572ce19 111
0997e51a 112 if (empty($this->data)) { // We need to define this properly
113 if (!empty($data)) {
114 if (is_object($data)) {
115 $this->data = $data; // Programmer knows what they are doing, we hope
9c00b5d7 116 } else if (!$this->data = $DB->get_record('data', array('id'=>$data))) {
29c1bb15 117 print_error('invalidid', 'data');
0997e51a 118 }
119 } else { // No way to define it!
29c1bb15 120 print_error('missingdata', 'data');
0997e51a 121 }
122 }
b572ce19 123
8429163d 124 if ($cm) {
125 $this->cm = $cm;
126 } else {
127 $this->cm = get_coursemodule_from_instance('data', $this->data->id);
128 }
129
0997e51a 130 if (empty($this->field)) { // We need to define some default values
131 $this->define_default_field();
132 }
8429163d 133
0b3b8c30 134 $this->context = context_module::instance($this->cm->id);
3d4b223a 135 }
0997e51a 136
6403e679 137
4636bf83 138 /**
139 * This field just sets up a default field object
140 *
141 * @return bool
142 */
0997e51a 143 function define_default_field() {
4102b449 144 global $OUTPUT;
0997e51a 145 if (empty($this->data->id)) {
4102b449 146 echo $OUTPUT->notification('Programmer error: dataid not defined in field class');
0997e51a 147 }
39790bd8 148 $this->field = new stdClass();
0997e51a 149 $this->field->id = 0;
150 $this->field->dataid = $this->data->id;
151 $this->field->type = $this->type;
152 $this->field->param1 = '';
153 $this->field->param2 = '';
154 $this->field->param3 = '';
155 $this->field->name = '';
156 $this->field->description = '';
b89cca19 157 $this->field->required = false;
b572ce19 158
0997e51a 159 return true;
3d4b223a 160 }
0997e51a 161
4636bf83 162 /**
163 * Set up the field object according to data in an object. Now is the time to clean it!
164 *
165 * @return bool
166 */
0997e51a 167 function define_field($data) {
168 $this->field->type = $this->type;
169 $this->field->dataid = $this->data->id;
170
171 $this->field->name = trim($data->name);
172 $this->field->description = trim($data->description);
1c3b2058 173 $this->field->required = !empty($data->required) ? 1 : 0;
0997e51a 174
175 if (isset($data->param1)) {
176 $this->field->param1 = trim($data->param1);
177 }
178 if (isset($data->param2)) {
8921fdb7 179 $this->field->param2 = trim($data->param2);
0997e51a 180 }
181 if (isset($data->param3)) {
182 $this->field->param3 = trim($data->param3);
183 }
184 if (isset($data->param4)) {
185 $this->field->param4 = trim($data->param4);
186 }
187 if (isset($data->param5)) {
188 $this->field->param5 = trim($data->param5);
189 }
190
191 return true;
3d4b223a 192 }
6403e679 193
4636bf83 194 /**
195 * Insert a new field in the database
196 * We assume the field object is already defined as $this->field
197 *
198 * @global object
199 * @return bool
200 */
0997e51a 201 function insert_field() {
4102b449 202 global $DB, $OUTPUT;
9c00b5d7 203
0997e51a 204 if (empty($this->field)) {
4102b449 205 echo $OUTPUT->notification('Programmer error: Field has not been defined yet! See define_field()');
0997e51a 206 return false;
207 }
208
a8f3a651 209 $this->field->id = $DB->insert_record('data_fields',$this->field);
e0d7ed6d
MN
210
211 // Trigger an event for creating this field.
212 $event = \mod_data\event\field_created::create(array(
213 'objectid' => $this->field->id,
214 'context' => $this->context,
215 'other' => array(
216 'fieldname' => $this->field->name,
217 'dataid' => $this->data->id
218 )
219 ));
220 $event->trigger();
221
0997e51a 222 return true;
3d4b223a 223 }
224
0997e51a 225
4636bf83 226 /**
227 * Update a field in the database
228 *
229 * @global object
230 * @return bool
231 */
0997e51a 232 function update_field() {
9c00b5d7 233 global $DB;
234
9d749339 235 $DB->update_record('data_fields', $this->field);
d1ec5557
MN
236
237 // Trigger an event for updating this field.
238 $event = \mod_data\event\field_updated::create(array(
239 'objectid' => $this->field->id,
240 'context' => $this->context,
241 'other' => array(
242 'fieldname' => $this->field->name,
243 'dataid' => $this->data->id
244 )
245 ));
246 $event->trigger();
247
0997e51a 248 return true;
249 }
3d4b223a 250
4636bf83 251 /**
252 * Delete a field completely
253 *
254 * @global object
255 * @return bool
256 */
0997e51a 257 function delete_field() {
9c00b5d7 258 global $DB;
259
0997e51a 260 if (!empty($this->field->id)) {
e37c413e
MN
261 // Get the field before we delete it.
262 $field = $DB->get_record('data_fields', array('id' => $this->field->id));
263
0997e51a 264 $this->delete_content();
8429163d 265 $DB->delete_records('data_fields', array('id'=>$this->field->id));
e37c413e
MN
266
267 // Trigger an event for deleting this field.
268 $event = \mod_data\event\field_deleted::create(array(
269 'objectid' => $this->field->id,
270 'context' => $this->context,
271 'other' => array(
272 'fieldname' => $this->field->name,
273 'dataid' => $this->data->id
274 )
275 ));
276 $event->add_record_snapshot('data_fields', $field);
277 $event->trigger();
3d4b223a 278 }
e37c413e 279
0997e51a 280 return true;
281 }
282
4636bf83 283 /**
284 * Print the relevant form element in the ADD template for this field
285 *
286 * @global object
287 * @param int $recordid
288 * @return string
289 */
1c3b2058
JO
290 function display_add_field($recordid=0, $formdata=null) {
291 global $DB, $OUTPUT;
9c00b5d7 292
b89cca19
DW
293 if ($formdata) {
294 $fieldname = 'field_' . $this->field->id;
295 $content = $formdata->$fieldname;
1c3b2058 296 } else if ($recordid) {
9c00b5d7 297 $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid));
0997e51a 298 } else {
3d4b223a 299 $content = '';
300 }
6403e679 301
344e15e6 302 // beware get_field returns false for new, empty records MDL-18567
303 if ($content===false) {
304 $content='';
305 }
306
1c3b2058
JO
307 $str = '<div title="' . s($this->field->description) . '">';
308 $str .= '<label for="field_'.$this->field->id.'"><span class="accesshide">'.$this->field->name.'</span>';
b89cca19 309 if ($this->field->required) {
663640f5 310 $image = $OUTPUT->pix_icon('req', get_string('requiredelement', 'form'));
c07c5e7b 311 $str .= html_writer::div($image, 'inline-req');
b89cca19 312 }
398be7c8
DW
313 $str .= '</label><input class="basefieldinput form-control d-inline mod-data-input" ' .
314 'type="text" name="field_' . $this->field->id . '" ' .
315 'id="field_' . $this->field->id . '" value="' . s($content) . '" />';
bbe39b6c 316 $str .= '</div>';
0997e51a 317
3d4b223a 318 return $str;
319 }
320
4636bf83 321 /**
322 * Print the relevant form element to define the attributes for this field
323 * viewable by teachers only.
324 *
325 * @global object
326 * @global object
327 * @return void Output is echo'd
328 */
0997e51a 329 function display_edit_field() {
b2dc6880 330 global $CFG, $DB, $OUTPUT;
0997e51a 331
332 if (empty($this->field)) { // No field has been defined yet, try and make one
333 $this->define_default_field();
3d4b223a 334 }
4102b449 335 echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
0997e51a 336
b7dc2256 337 echo '<form id="editfield" action="'.$CFG->wwwroot.'/mod/data/field.php" method="post">'."\n";
0997e51a 338 echo '<input type="hidden" name="d" value="'.$this->data->id.'" />'."\n";
339 if (empty($this->field->id)) {
340 echo '<input type="hidden" name="mode" value="add" />'."\n";
341 $savebutton = get_string('add');
342 } else {
343 echo '<input type="hidden" name="fid" value="'.$this->field->id.'" />'."\n";
344 echo '<input type="hidden" name="mode" value="update" />'."\n";
345 $savebutton = get_string('savechanges');
346 }
347 echo '<input type="hidden" name="type" value="'.$this->type.'" />'."\n";
348 echo '<input name="sesskey" value="'.sesskey().'" type="hidden" />'."\n";
6403e679 349
66e2b9f8 350 echo $OUTPUT->heading($this->name(), 3);
0997e51a 351
3d4b223a 352 require_once($CFG->dirroot.'/mod/data/field/'.$this->type.'/mod.html');
0997e51a 353
85db96c5 354 echo '<div class="mdl-align">';
e85fda95
DW
355 echo '<input type="submit" class="btn btn-primary" value="'.$savebutton.'" />'."\n";
356 echo '<input type="submit" class="btn btn-secondary" name="cancel" value="'.get_string('cancel').'" />'."\n";
e357c206 357 echo '</div>';
0997e51a 358
359 echo '</form>';
360
4102b449 361 echo $OUTPUT->box_end();
3d4b223a 362 }
6403e679 363
4636bf83 364 /**
365 * Display the content of the field in browse mode
366 *
367 * @global object
368 * @param int $recordid
369 * @param object $template
370 * @return bool|string
371 */
0997e51a 372 function display_browse_field($recordid, $template) {
9c00b5d7 373 global $DB;
374
375 if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
6403e679 376 if (isset($content->content)) {
39790bd8 377 $options = new stdClass();
c8505cac 378 if ($this->field->param1 == '1') { // We are autolinking this field, so disable linking within us
379 //$content->content = '<span class="nolink">'.$content->content.'</span>';
380 //$content->content1 = FORMAT_HTML;
381 $options->filter=false;
382 }
1f697b99 383 $options->para = false;
384 $str = format_text($content->content, $content->content1, $options);
3d4b223a 385 } else {
386 $str = '';
387 }
388 return $str;
389 }
390 return false;
391 }
6403e679 392
4636bf83 393 /**
394 * Update the content of one data field in the data_content table
395 * @global object
396 * @param int $recordid
397 * @param mixed $value
398 * @param string $name
399 * @return bool
400 */
0997e51a 401 function update_content($recordid, $value, $name=''){
9c00b5d7 402 global $DB;
403
39790bd8 404 $content = new stdClass();
0997e51a 405 $content->fieldid = $this->field->id;
c87fbb27 406 $content->recordid = $recordid;
407 $content->content = clean_param($value, PARAM_NOTAGS);
0997e51a 408
9c00b5d7 409 if ($oldcontent = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
3d4b223a 410 $content->id = $oldcontent->id;
9c00b5d7 411 return $DB->update_record('data_content', $content);
0997e51a 412 } else {
9c00b5d7 413 return $DB->insert_record('data_content', $content);
3d4b223a 414 }
0997e51a 415 }
6403e679 416
4636bf83 417 /**
418 * Delete all content associated with the field
419 *
420 * @global object
421 * @param int $recordid
422 * @return bool
423 */
0997e51a 424 function delete_content($recordid=0) {
9c00b5d7 425 global $DB;
0997e51a 426
0997e51a 427 if ($recordid) {
8429163d 428 $conditions = array('fieldid'=>$this->field->id, 'recordid'=>$recordid);
0997e51a 429 } else {
8429163d 430 $conditions = array('fieldid'=>$this->field->id);
3d4b223a 431 }
0997e51a 432
6b1b1d03
EL
433 $rs = $DB->get_recordset('data_content', $conditions);
434 if ($rs->valid()) {
8429163d 435 $fs = get_file_storage();
436 foreach ($rs as $content) {
64f93798 437 $fs->delete_area_files($this->context->id, 'mod_data', 'content', $content->id);
8429163d 438 }
0997e51a 439 }
6b1b1d03 440 $rs->close();
0997e51a 441
8429163d 442 return $DB->delete_records('data_content', $conditions);
0997e51a 443 }
6403e679 444
4636bf83 445 /**
446 * Check if a field from an add form is empty
447 *
448 * @param mixed $value
449 * @param mixed $name
450 * @return bool
451 */
f9eab7b0 452 function notemptyfield($value, $name) {
3d4b223a 453 return !empty($value);
454 }
6403e679 455
4636bf83 456 /**
457 * Just in case a field needs to print something before the whole form
458 */
0997e51a 459 function print_before_form() {
3d4b223a 460 }
0997e51a 461
4636bf83 462 /**
463 * Just in case a field needs to print something after the whole form
464 */
5f5bcda8 465 function print_after_form() {
5f5bcda8 466 }
6403e679 467
468
4636bf83 469 /**
470 * Returns the sortable field for the content. By default, it's just content
471 * but for some plugins, it could be content 1 - content4
472 *
473 * @return string
474 */
cf3e199b 475 function get_sort_field() {
476 return 'content';
477 }
0997e51a 478
4636bf83 479 /**
480 * Returns the SQL needed to refer to the column. Some fields may need to CAST() etc.
481 *
482 * @param string $fieldname
483 * @return string $fieldname
484 */
64452eb4 485 function get_sort_sql($fieldname) {
486 return $fieldname;
487 }
488
4636bf83 489 /**
490 * Returns the name/type of the field
491 *
492 * @return string
493 */
b8b554ac 494 function name() {
421b2d44 495 return get_string('fieldtypelabel', "datafield_$this->type");
0997e51a 496 }
6403e679 497
4636bf83 498 /**
499 * Prints the respective type icon
500 *
501 * @global object
502 * @return string
503 */
0997e51a 504 function image() {
5ef44400 505 global $OUTPUT;
0997e51a 506
a7d4cb67
DC
507 $params = array('d'=>$this->data->id, 'fid'=>$this->field->id, 'mode'=>'display', 'sesskey'=>sesskey());
508 $link = new moodle_url('/mod/data/field.php', $params);
509 $str = '<a href="'.$link->out().'">';
b9b409cf 510 $str .= $OUTPUT->pix_icon('field/' . $this->type, $this->type, 'data');
663640f5 511 $str .= '</a>';
0997e51a 512 return $str;
513 }
514
4636bf83 515 /**
516 * Per default, it is assumed that fields support text exporting.
517 * Override this (return false) on fields not supporting text exporting.
518 *
519 * @return bool true
520 */
b8b554ac 521 function text_export_supported() {
522 return true;
523 }
6403e679 524
4636bf83 525 /**
526 * Per default, return the record's text value only from the "content" field.
527 * Override this in fields class if necesarry.
528 *
529 * @param string $record
530 * @return string
531 */
b8b554ac 532 function export_text_value($record) {
533 if ($this->text_export_supported()) {
534 return $record->content;
535 }
536 }
0997e51a 537
4636bf83 538 /**
539 * @param string $relativepath
540 * @return bool false
541 */
8429163d 542 function file_ok($relativepath) {
543 return false;
544 }
2af9ad7d
DG
545
546 /**
547 * Returns the priority for being indexed by globalsearch
548 *
549 * @return int
550 */
551 public static function get_priority() {
552 return static::$priority;
553 }
554
555 /**
556 * Returns the presentable string value for a field content.
fa90f529
DM
557 *
558 * The returned string should be plain text.
559 *
560 * @param stdClass $content
2af9ad7d
DG
561 * @return string
562 */
fa90f529
DM
563 public static function get_content_value($content) {
564 return trim($content->content, "\r\n ");
2af9ad7d 565 }
aefe9c5f
JL
566
567 /**
568 * Return the plugin configs for external functions,
569 * in some cases the configs will need formatting or be returned only if the current user has some capabilities enabled.
570 *
571 * @return array the list of config parameters
572 * @since Moodle 3.3
573 */
574 public function get_config_for_external() {
575 // Return all the field configs to null (maybe there is a private key for a service or something similar there).
576 $configs = [];
577 for ($i = 1; $i <= 10; $i++) {
578 $configs["param$i"] = null;
579 }
580 return $configs;
581 }
b8b554ac 582}
3d4b223a 583
584
4636bf83 585/**
586 * Given a template and a dataid, generate a default case template
587 *
588 * @global object
589 * @param object $data
590 * @param string template [addtemplate, singletemplate, listtempalte, rsstemplate]
591 * @param int $recordid
592 * @param bool $form
593 * @param bool $update
594 * @return bool|string
595 */
a44e7081 596function data_generate_default_template(&$data, $template, $recordid=0, $form=false, $update=true) {
9c00b5d7 597 global $DB;
d118d06a 598
edaa546a 599 if (!$data && !$template) {
3d4b223a 600 return false;
601 }
5cd07964 602 if ($template == 'csstemplate' or $template == 'jstemplate' ) {
f24eb261 603 return '';
604 }
6403e679 605
b8b554ac 606 // get all the fields for that database
9c00b5d7 607 if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'id')) {
6403e679 608
b41fc49c 609 $table = new html_table();
0bec64c6 610 $table->attributes['class'] = 'mod-data-default-template ##approvalstatus##';
b41fc49c
NK
611 $table->colclasses = array('template-field', 'template-token');
612 $table->data = array();
f41cadeb 613 foreach ($fields as $field) {
b8b554ac 614 if ($form) { // Print forms instead of data
d118d06a 615 $fieldobj = data_get_field($field, $data);
b89cca19 616 $token = $fieldobj->display_add_field($recordid, null);
b8b554ac 617 } else { // Just print the tag
b41fc49c 618 $token = '[['.$field->name.']]';
d118d06a 619 }
b41fc49c
NK
620 $table->data[] = array(
621 $field->name.': ',
622 $token
623 );
3d4b223a 624 }
ca5f3e0a
AH
625
626 if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
627 $label = new html_table_cell(get_string('tags') . ':');
628 if ($form) {
629 $cell = data_generate_tag_form();
630 } else {
631 $cell = new html_table_cell('##tags##');
632 }
633 $table->data[] = new html_table_row(array($label, $cell));
634 }
635
bb828644 636 if ($template == 'listtemplate') {
d0372ed6 637 $cell = new html_table_cell('##edit## ##more## ##delete## ##approve## ##disapprove## ##export##');
b41fc49c
NK
638 $cell->colspan = 2;
639 $cell->attributes['class'] = 'controls';
640 $table->data[] = new html_table_row(array($cell));
bb828644 641 } else if ($template == 'singletemplate') {
d0372ed6 642 $cell = new html_table_cell('##edit## ##delete## ##approve## ##disapprove## ##export##');
b41fc49c
NK
643 $cell->colspan = 2;
644 $cell->attributes['class'] = 'controls';
645 $table->data[] = new html_table_row(array($cell));
714bec74 646 } else if ($template == 'asearchtemplate') {
2b04c41c 647 $row = new html_table_row(array(get_string('authorfirstname', 'data').': ', '##firstname##'));
b41fc49c
NK
648 $row->attributes['class'] = 'searchcontrols';
649 $table->data[] = $row;
2b04c41c 650 $row = new html_table_row(array(get_string('authorlastname', 'data').': ', '##lastname##'));
b41fc49c
NK
651 $row->attributes['class'] = 'searchcontrols';
652 $table->data[] = $row;
3d4b223a 653 }
654
cf9ecfda
AG
655 $str = '';
656 if ($template == 'listtemplate'){
657 $str .= '##delcheck##';
658 $str .= html_writer::empty_tag('br');
659 }
660
661 $str .= html_writer::start_tag('div', array('class' => 'defaulttemplate'));
b41fc49c
NK
662 $str .= html_writer::table($table);
663 $str .= html_writer::end_tag('div');
d118d06a 664 if ($template == 'listtemplate'){
b41fc49c 665 $str .= html_writer::empty_tag('hr');
3d4b223a 666 }
f9eab7b0 667
d118d06a 668 if ($update) {
39790bd8 669 $newdata = new stdClass();
d118d06a 670 $newdata->id = $data->id;
f41cadeb 671 $newdata->{$template} = $str;
9d749339 672 $DB->update_record('data', $newdata);
673 $data->{$template} = $str;
3d4b223a 674 }
d118d06a 675
676 return $str;
3d4b223a 677 }
678}
679
ca5f3e0a
AH
680/**
681 * Build the form elements to manage tags for a record.
682 *
683 * @param int $recordid
684 * @return string
685 */
686function data_generate_tag_form($recordid) {
687 global $CFG, $DB, $PAGE;
688
689 $tagtypestoshow = \core_tag_area::get_showstandard('mod_data', 'data_records');
690 $showstandard = ($tagtypestoshow != core_tag_tag::HIDE_STANDARD);
691 $typenewtags = ($tagtypestoshow != core_tag_tag::STANDARD_ONLY);
692
693 $str = html_writer::start_tag('div', array('class' => 'datatagcontrol'));
694
695 $namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
696
697 $existingtags = core_tag_tag::get_item_tags_array('mod_data', 'data_records', $recordid);
698
699 if ($showstandard) {
700 $tags = $DB->get_records_menu('tag', array(
701 'isstandard' => 1,
702 'tagcollid' => \core_tag_area::get_collection('mod_data', 'data_records')
703 ), $namefield, 'id,' . $namefield . ' as fieldname');
704
705 $tags = $existingtags + $tags;
706 } else {
707 $tags = $existingtags;
708 }
709
710 $str .= '<select class="custom-select" name="tags[]" id="tags" multiple>';
711 foreach ($tags as $tagid => $tag) {
712 $selected = key_exists($tagid, $existingtags) ? 'selected' : '';
713 $str .= "<option value='$tag' $selected>$tag</option>";
714 }
715 $str .= '</select>';
716
717 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array(
718 '#tags',
719 $typenewtags,
720 '',
721 get_string('entertags', 'tag'),
722 false,
723 $showstandard,
724 get_string('noselection', 'form')
725 )
726 );
727
728 $str .= html_writer::end_tag('div');
729
730 return $str;
731}
732
3d4b223a 733
4636bf83 734/**
56c1ca88 735 * Search for a field name and replaces it with another one in all the
736 * form templates. Set $newfieldname as '' if you want to delete the
4636bf83 737 * field from the form.
738 *
739 * @global object
740 * @param object $data
741 * @param string $searchfieldname
742 * @param string $newfieldname
743 * @return bool
744 */
0997e51a 745function data_replace_field_in_templates($data, $searchfieldname, $newfieldname) {
9c00b5d7 746 global $DB;
747
f9eab7b0 748 if (!empty($newfieldname)) {
749 $prestring = '[[';
750 $poststring = ']]';
9706fa56 751 $idpart = '#id';
735a7952 752
753 } else {
f9eab7b0 754 $prestring = '';
755 $poststring = '';
9706fa56 756 $idpart = '';
f9eab7b0 757 }
6403e679 758
39790bd8 759 $newdata = new stdClass();
735a7952 760 $newdata->id = $data->id;
9c00b5d7 761 $newdata->singletemplate = str_ireplace('[['.$searchfieldname.']]',
762 $prestring.$newfieldname.$poststring, $data->singletemplate);
6403e679 763
9c00b5d7 764 $newdata->listtemplate = str_ireplace('[['.$searchfieldname.']]',
765 $prestring.$newfieldname.$poststring, $data->listtemplate);
6403e679 766
9c00b5d7 767 $newdata->addtemplate = str_ireplace('[['.$searchfieldname.']]',
768 $prestring.$newfieldname.$poststring, $data->addtemplate);
6403e679 769
9c00b5d7 770 $newdata->addtemplate = str_ireplace('[['.$searchfieldname.'#id]]',
771 $prestring.$newfieldname.$idpart.$poststring, $data->addtemplate);
9706fa56 772
9c00b5d7 773 $newdata->rsstemplate = str_ireplace('[['.$searchfieldname.']]',
774 $prestring.$newfieldname.$poststring, $data->rsstemplate);
6403e679 775
9c00b5d7 776 return $DB->update_record('data', $newdata);
f9eab7b0 777}
778
779
4636bf83 780/**
781 * Appends a new field at the end of the form template.
782 *
783 * @global object
784 * @param object $data
785 * @param string $newfieldname
786 */
0997e51a 787function data_append_new_field_to_templates($data, $newfieldname) {
9c00b5d7 788 global $DB;
0997e51a 789
39790bd8 790 $newdata = new stdClass();
0997e51a 791 $newdata->id = $data->id;
ed69c723 792 $change = false;
0997e51a 793
f9eab7b0 794 if (!empty($data->singletemplate)) {
9c00b5d7 795 $newdata->singletemplate = $data->singletemplate.' [[' . $newfieldname .']]';
ed69c723 796 $change = true;
0997e51a 797 }
798 if (!empty($data->addtemplate)) {
9c00b5d7 799 $newdata->addtemplate = $data->addtemplate.' [[' . $newfieldname . ']]';
ed69c723 800 $change = true;
f9eab7b0 801 }
802 if (!empty($data->rsstemplate)) {
9c00b5d7 803 $newdata->rsstemplate = $data->singletemplate.' [[' . $newfieldname . ']]';
ed69c723 804 $change = true;
805 }
806 if ($change) {
9c00b5d7 807 $DB->update_record('data', $newdata);
f9eab7b0 808 }
f9eab7b0 809}
810
811
4636bf83 812/**
813 * given a field name
814 * this function creates an instance of the particular subfield class
815 *
816 * @global object
817 * @param string $name
818 * @param object $data
819 * @return object|bool
820 */
0997e51a 821function data_get_field_from_name($name, $data){
9c00b5d7 822 global $DB;
823
1e123f47 824 $field = $DB->get_record('data_fields', array('name'=>$name, 'dataid'=>$data->id));
0997e51a 825
826 if ($field) {
827 return data_get_field($field, $data);
828 } else {
829 return false;
830 }
831}
832
4636bf83 833/**
834 * given a field id
835 * this function creates an instance of the particular subfield class
836 *
837 * @global object
838 * @param int $fieldid
839 * @param object $data
840 * @return bool|object
841 */
0997e51a 842function data_get_field_from_id($fieldid, $data){
9c00b5d7 843 global $DB;
844
1e123f47 845 $field = $DB->get_record('data_fields', array('id'=>$fieldid, 'dataid'=>$data->id));
3d4b223a 846
0997e51a 847 if ($field) {
848 return data_get_field($field, $data);
5782be6b 849 } else {
d6f0e247 850 return false;
3d4b223a 851 }
3d4b223a 852}
853
4636bf83 854/**
855 * given a field id
856 * this function creates an instance of the particular subfield class
857 *
858 * @global object
859 * @param string $type
860 * @param object $data
861 * @return object
862 */
0997e51a 863function data_get_field_new($type, $data) {
864 global $CFG;
b572ce19 865
0997e51a 866 require_once($CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php');
867 $newfield = 'data_field_'.$type;
868 $newfield = new $newfield(0, $data);
869 return $newfield;
870}
871
4636bf83 872/**
873 * returns a subclass field object given a record of the field, used to
874 * invoke plugin methods
875 * input: $param $field - record from db
876 *
877 * @global object
878 * @param object $field
879 * @param object $data
880 * @param object $cm
881 * @return object
882 */
8429163d 883function data_get_field($field, $data, $cm=null) {
0997e51a 884 global $CFG;
b572ce19 885
0997e51a 886 if ($field) {
3d4b223a 887 require_once('field/'.$field->type.'/field.class.php');
888 $newfield = 'data_field_'.$field->type;
8429163d 889 $newfield = new $newfield($field, $data, $cm);
3d4b223a 890 return $newfield;
891 }
892}
893
894
8429163d 895/**
896 * Given record object (or id), returns true if the record belongs to the current user
4636bf83 897 *
898 * @global object
899 * @global object
900 * @param mixed $record record object or id
8429163d 901 * @return bool
902 */
903function data_isowner($record) {
9c00b5d7 904 global $USER, $DB;
3d4b223a 905
4f0c2d00 906 if (!isloggedin()) { // perf shortcut
3d4b223a 907 return false;
908 }
909
8429163d 910 if (!is_object($record)) {
911 if (!$record = $DB->get_record('data_records', array('id'=>$record))) {
912 return false;
913 }
3d4b223a 914 }
915
8429163d 916 return ($record->userid == $USER->id);
3d4b223a 917}
918
4636bf83 919/**
920 * has a user reached the max number of entries?
921 *
922 * @param object $data
923 * @return bool
924 */
b572ce19 925function data_atmaxentries($data){
926 if (!$data->maxentries){
3d4b223a 927 return false;
b572ce19 928
3d4b223a 929 } else {
930 return (data_numentries($data) >= $data->maxentries);
931 }
932}
933
4636bf83 934/**
935 * returns the number of entries already made by this user
56c1ca88 936 *
4636bf83 937 * @global object
938 * @global object
56c1ca88 939 * @param object $data
4636bf83 940 * @return int
941 */
f9729dcf 942function data_numentries($data, $userid=null) {
8429163d 943 global $USER, $DB;
f9729dcf
MG
944 if ($userid === null) {
945 $userid = $USER->id;
946 }
9c00b5d7 947 $sql = 'SELECT COUNT(*) FROM {data_records} WHERE dataid=? AND userid=?';
f9729dcf 948 return $DB->count_records_sql($sql, array($data->id, $userid));
3d4b223a 949}
950
4636bf83 951/**
56c1ca88 952 * function that takes in a dataid and adds a record
4636bf83 953 * this is used everytime an add template is submitted
954 *
955 * @global object
956 * @global object
957 * @param object $data
958 * @param int $groupid
959 * @return bool
960 */
0997e51a 961function data_add_record($data, $groupid=0){
9c00b5d7 962 global $USER, $DB;
6403e679 963
c088d603 964 $cm = get_coursemodule_from_instance('data', $data->id);
0b3b8c30 965 $context = context_module::instance($cm->id);
b572ce19 966
39790bd8 967 $record = new stdClass();
3d4b223a 968 $record->userid = $USER->id;
0997e51a 969 $record->dataid = $data->id;
3d4b223a 970 $record->groupid = $groupid;
0997e51a 971 $record->timecreated = $record->timemodified = time();
0468976c 972 if (has_capability('mod/data:approve', $context)) {
6e0119dd 973 $record->approved = 1;
974 } else {
975 $record->approved = 0;
976 }
76ca452c
MN
977 $record->id = $DB->insert_record('data_records', $record);
978
979 // Trigger an event for creating this record.
980 $event = \mod_data\event\record_created::create(array(
981 'objectid' => $record->id,
982 'context' => $context,
983 'other' => array(
984 'dataid' => $data->id
985 )
986 ));
987 $event->trigger();
988
f9729dcf 989 $course = get_course($cm->course);
cf873ada 990 data_update_completion_state($data, $course, $cm);
f9729dcf 991
76ca452c 992 return $record->id;
3d4b223a 993}
994
4636bf83 995/**
996 * check the multple existence any tag in a template
997 *
998 * check to see if there are 2 or more of the same tag being used.
999 *
1000 * @global object
1001 * @param int $dataid,
56c1ca88 1002 * @param string $template
4636bf83 1003 * @return bool
1004 */
9c00b5d7 1005function data_tags_check($dataid, $template) {
4102b449 1006 global $DB, $OUTPUT;
9c00b5d7 1007
b8b554ac 1008 // first get all the possible tags
9c00b5d7 1009 $fields = $DB->get_records('data_fields', array('dataid'=>$dataid));
b8b554ac 1010 // then we generate strings to replace
1011 $tagsok = true; // let's be optimistic
b572ce19 1012 foreach ($fields as $field){
546a58bc 1013 $pattern="/\[\[" . preg_quote($field->name, '/') . "\]\]/i";
b572ce19 1014 if (preg_match_all($pattern, $template, $dummy)>1){
3d4b223a 1015 $tagsok = false;
4102b449 1016 echo $OUTPUT->notification('[['.$field->name.']] - '.get_string('multipletags','data'));
3d4b223a 1017 }
1018 }
b8b554ac 1019 // else return true
3d4b223a 1020 return $tagsok;
1021}
1022
4636bf83 1023/**
1024 * Adds an instance of a data
1025 *
6b04fdc0
PS
1026 * @param stdClass $data
1027 * @param mod_data_mod_form $mform
1028 * @return int intance id
4636bf83 1029 */
6b04fdc0 1030function data_add_instance($data, $mform = null) {
87d6e85a
SB
1031 global $DB, $CFG;
1032 require_once($CFG->dirroot.'/mod/data/locallib.php');
3d4b223a 1033
57244db3 1034 if (empty($data->assessed)) {
1035 $data->assessed = 0;
d6af3cfa 1036 }
1037
f3013ba6
AG
1038 if (empty($data->ratingtime) || empty($data->assessed)) {
1039 $data->assesstimestart = 0;
1040 $data->assesstimefinish = 0;
1041 }
1042
3d4b223a 1043 $data->timemodified = time();
1044
a8f3a651 1045 $data->id = $DB->insert_record('data', $data);
3d4b223a 1046
87d6e85a
SB
1047 // Add calendar events if necessary.
1048 data_set_events($data);
9ffbdafc
AG
1049 if (!empty($data->completionexpected)) {
1050 \core_completion\api::update_completion_date_event($data->coursemodule, 'data', $data->id, $data->completionexpected);
1051 }
87d6e85a 1052
612607bd 1053 data_grade_item_update($data);
b572ce19 1054
3d4b223a 1055 return $data->id;
1056}
1057
4636bf83 1058/**
1059 * updates an instance of a data
1060 *
1061 * @global object
1062 * @param object $data
1063 * @return bool
1064 */
3d4b223a 1065function data_update_instance($data) {
87d6e85a
SB
1066 global $DB, $CFG;
1067 require_once($CFG->dirroot.'/mod/data/locallib.php');
6403e679 1068
04366d79 1069 $data->timemodified = time();
b572ce19 1070 $data->id = $data->instance;
6403e679 1071
57244db3 1072 if (empty($data->assessed)) {
1073 $data->assessed = 0;
17e5f3fc 1074 }
b572ce19 1075
d251b259
AD
1076 if (empty($data->ratingtime) or empty($data->assessed)) {
1077 $data->assesstimestart = 0;
1078 $data->assesstimefinish = 0;
1079 }
1080
05ac14ca 1081 if (empty($data->notification)) {
1082 $data->notification = 0;
1083 }
1084
0bcf8b6f 1085 $DB->update_record('data', $data);
04366d79 1086
87d6e85a
SB
1087 // Add calendar events if necessary.
1088 data_set_events($data);
9ffbdafc
AG
1089 $completionexpected = (!empty($data->completionexpected)) ? $data->completionexpected : null;
1090 \core_completion\api::update_completion_date_event($data->coursemodule, 'data', $data->id, $completionexpected);
87d6e85a 1091
04366d79 1092 data_grade_item_update($data);
b572ce19 1093
04366d79 1094 return true;
b572ce19 1095
3d4b223a 1096}
1097
4636bf83 1098/**
1099 * deletes an instance of a data
1100 *
1101 * @global object
1102 * @param int $id
1103 * @return bool
1104 */
b8b554ac 1105function data_delete_instance($id) { // takes the dataid
650a0c0a 1106 global $DB, $CFG;
3d4b223a 1107
8429163d 1108 if (!$data = $DB->get_record('data', array('id'=>$id))) {
3d4b223a 1109 return false;
1110 }
1111
8429163d 1112 $cm = get_coursemodule_from_instance('data', $data->id);
0b3b8c30 1113 $context = context_module::instance($cm->id);
b572ce19 1114
8429163d 1115/// Delete all the associated information
6403e679 1116
8429163d 1117 // files
1118 $fs = get_file_storage();
64f93798 1119 $fs->delete_area_files($context->id, 'mod_data');
3d4b223a 1120
8429163d 1121 // get all the records in this data
1122 $sql = "SELECT r.id
1123 FROM {data_records} r
1124 WHERE r.dataid = ?";
6403e679 1125
8429163d 1126 $DB->delete_records_select('data_content', "recordid IN ($sql)", array($id));
3d4b223a 1127
1128 // delete all the records and fields
c18269c7 1129 $DB->delete_records('data_records', array('dataid'=>$id));
1130 $DB->delete_records('data_fields', array('dataid'=>$id));
3d4b223a 1131
87d6e85a
SB
1132 // Remove old calendar events.
1133 $events = $DB->get_records('event', array('modulename' => 'data', 'instance' => $id));
1134 foreach ($events as $event) {
1135 $event = calendar_event::load($event);
1136 $event->delete();
1137 }
1138
3d4b223a 1139 // Delete the instance itself
c18269c7 1140 $result = $DB->delete_records('data', array('id'=>$id));
04366d79 1141
8429163d 1142 // cleanup gradebook
b82cacea 1143 data_grade_item_delete($data);
b572ce19 1144
04366d79 1145 return $result;
3d4b223a 1146}
1147
4636bf83 1148/**
1149 * returns a summary of data activity of this user
1150 *
1151 * @global object
1152 * @param object $course
1153 * @param object $user
1154 * @param object $mod
1155 * @param object $data
1156 * @return object|null
1157 */
3d4b223a 1158function data_user_outline($course, $user, $mod, $data) {
1a96363a
NC
1159 global $DB, $CFG;
1160 require_once("$CFG->libdir/gradelib.php");
1161
1162 $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
1163 if (empty($grades->items[0]->grades)) {
1164 $grade = false;
1165 } else {
1166 $grade = reset($grades->items[0]->grades);
1167 }
1168
9c00b5d7 1169
1170 if ($countrecords = $DB->count_records('data_records', array('dataid'=>$data->id, 'userid'=>$user->id))) {
39790bd8 1171 $result = new stdClass();
63701c78 1172 $result->info = get_string('numrecords', 'data', $countrecords);
9c00b5d7 1173 $lastrecord = $DB->get_record_sql('SELECT id,timemodified FROM {data_records}
1174 WHERE dataid = ? AND userid = ?
1175 ORDER BY timemodified DESC', array($data->id, $user->id), true);
3d4b223a 1176 $result->time = $lastrecord->timemodified;
1a96363a
NC
1177 if ($grade) {
1178 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
1179 }
1180 return $result;
1181 } else if ($grade) {
39790bd8 1182 $result = new stdClass();
1a96363a 1183 $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
4433f871
AD
1184
1185 //datesubmitted == time created. dategraded == time modified or time overridden
1186 //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
94a74f54 1187 //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
4433f871
AD
1188 if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
1189 $result->time = $grade->dategraded;
1190 } else {
1191 $result->time = $grade->datesubmitted;
1192 }
1193
3d4b223a 1194 return $result;
1195 }
1196 return NULL;
3d4b223a 1197}
1198
4636bf83 1199/**
1200 * Prints all the records uploaded by this user
1201 *
1202 * @global object
1203 * @param object $course
1204 * @param object $user
1205 * @param object $mod
1206 * @param object $data
1207 */
3d4b223a 1208function data_user_complete($course, $user, $mod, $data) {
1a96363a
NC
1209 global $DB, $CFG, $OUTPUT;
1210 require_once("$CFG->libdir/gradelib.php");
1211
1212 $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
1213 if (!empty($grades->items[0]->grades)) {
1214 $grade = reset($grades->items[0]->grades);
1215 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
1216 if ($grade->str_feedback) {
1217 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
1218 }
1219 }
9c00b5d7 1220
1221 if ($records = $DB->get_records('data_records', array('dataid'=>$data->id,'userid'=>$user->id), 'timemodified DESC')) {
3d45b8e5 1222 data_print_template('singletemplate', $records, $data);
3d4b223a 1223 }
1224}
1225
04366d79 1226/**
1227 * Return grade for given user or all users.
1228 *
4636bf83 1229 * @global object
1230 * @param object $data
04366d79 1231 * @param int $userid optional user id, 0 means all users
1232 * @return array array of grades, false if none
1233 */
612607bd 1234function data_get_user_grades($data, $userid=0) {
d251b259
AD
1235 global $CFG;
1236
1237 require_once($CFG->dirroot.'/rating/lib.php');
04366d79 1238
2b04c41c
SH
1239 $ratingoptions = new stdClass;
1240 $ratingoptions->component = 'mod_data';
1241 $ratingoptions->ratingarea = 'entry';
d251b259
AD
1242 $ratingoptions->modulename = 'data';
1243 $ratingoptions->moduleid = $data->id;
04366d79 1244
d251b259
AD
1245 $ratingoptions->userid = $userid;
1246 $ratingoptions->aggregationmethod = $data->assessed;
1247 $ratingoptions->scaleid = $data->scale;
1248 $ratingoptions->itemtable = 'data_records';
1249 $ratingoptions->itemtableusercolumn = 'userid';
04366d79 1250
2b04c41c 1251 $rm = new rating_manager();
d251b259 1252 return $rm->get_user_grades($ratingoptions);
04366d79 1253}
1254
1255/**
775f811a 1256 * Update activity grades
04366d79 1257 *
a153c9f2 1258 * @category grade
775f811a 1259 * @param object $data
1260 * @param int $userid specific user only, 0 means all
4636bf83 1261 * @param bool $nullifnone
04366d79 1262 */
775f811a 1263function data_update_grades($data, $userid=0, $nullifnone=true) {
9c00b5d7 1264 global $CFG, $DB;
1265 require_once($CFG->libdir.'/gradelib.php');
04366d79 1266
775f811a 1267 if (!$data->assessed) {
1268 data_grade_item_update($data);
b572ce19 1269
775f811a 1270 } else if ($grades = data_get_user_grades($data, $userid)) {
1271 data_grade_item_update($data, $grades);
b572ce19 1272
775f811a 1273 } else if ($userid and $nullifnone) {
39790bd8 1274 $grade = new stdClass();
775f811a 1275 $grade->userid = $userid;
1276 $grade->rawgrade = NULL;
1277 data_grade_item_update($data, $grade);
b572ce19 1278
04366d79 1279 } else {
775f811a 1280 data_grade_item_update($data);
1281 }
1282}
1283
04366d79 1284/**
612607bd 1285 * Update/create grade item for given data
04366d79 1286 *
a153c9f2
AD
1287 * @category grade
1288 * @param stdClass $data A database instance with extra cmidnumber property
1289 * @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook
04366d79 1290 * @return object grade_item
1291 */
0b5a80a1 1292function data_grade_item_update($data, $grades=NULL) {
612607bd 1293 global $CFG;
9c00b5d7 1294 require_once($CFG->libdir.'/gradelib.php');
04366d79 1295
13534ef7 1296 $params = array('itemname'=>$data->name, 'idnumber'=>$data->cmidnumber);
b572ce19 1297
04366d79 1298 if (!$data->assessed or $data->scale == 0) {
612607bd 1299 $params['gradetype'] = GRADE_TYPE_NONE;
b572ce19 1300
04366d79 1301 } else if ($data->scale > 0) {
1302 $params['gradetype'] = GRADE_TYPE_VALUE;
1303 $params['grademax'] = $data->scale;
1304 $params['grademin'] = 0;
b572ce19 1305
04366d79 1306 } else if ($data->scale < 0) {
1307 $params['gradetype'] = GRADE_TYPE_SCALE;
1308 $params['scaleid'] = -$data->scale;
1309 }
b572ce19 1310
0b5a80a1 1311 if ($grades === 'reset') {
1312 $params['reset'] = true;
1313 $grades = NULL;
1314 }
1315
1316 return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, $grades, $params);
04366d79 1317}
1318
1319/**
1320 * Delete grade item for given data
1321 *
a153c9f2 1322 * @category grade
04366d79 1323 * @param object $data object
1324 * @return object grade_item
1325 */
1326function data_grade_item_delete($data) {
612607bd 1327 global $CFG;
1328 require_once($CFG->libdir.'/gradelib.php');
b572ce19 1329
b67ec72f 1330 return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, NULL, array('deleted'=>1));
04366d79 1331}
1332
b8b554ac 1333// junk functions
4636bf83 1334/**
56c1ca88 1335 * takes a list of records, the current data, a search string,
4636bf83 1336 * and mode to display prints the translated template
1337 *
1338 * @global object
1339 * @global object
1340 * @param string $template
56c1ca88 1341 * @param array $records
4636bf83 1342 * @param object $data
56c1ca88 1343 * @param string $search
4636bf83 1344 * @param int $page
1345 * @param bool $return
f09040d7 1346 * @param object $jumpurl a moodle_url by which to jump back to the record list (can be null)
4636bf83 1347 * @return mixed
1348 */
f09040d7 1349function data_print_template($template, $records, $data, $search='', $page=0, $return=false, moodle_url $jumpurl=null) {
f2a1963c 1350 global $CFG, $DB, $OUTPUT;
cf9ecfda 1351
c088d603 1352 $cm = get_coursemodule_from_instance('data', $data->id);
0b3b8c30 1353 $context = context_module::instance($cm->id);
b572ce19 1354
2c216a6c
MN
1355 static $fields = array();
1356 static $dataid = null;
b572ce19 1357
63701c78 1358 if (empty($dataid)) {
1359 $dataid = $data->id;
1360 } else if ($dataid != $data->id) {
2c216a6c 1361 $fields = array();
63701c78 1362 }
b572ce19 1363
3d45b8e5 1364 if (empty($fields)) {
9c00b5d7 1365 $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id));
3d45b8e5 1366 foreach ($fieldrecords as $fieldrecord) {
1367 $fields[]= data_get_field($fieldrecord, $data);
1368 }
3d45b8e5 1369 }
b572ce19 1370
64452eb4 1371 if (empty($records)) {
1372 return;
1373 }
b572ce19 1374
f09040d7
HB
1375 if (!$jumpurl) {
1376 $jumpurl = new moodle_url('/mod/data/view.php', array('d' => $data->id));
1377 }
1378 $jumpurl = new moodle_url($jumpurl, array('page' => $page, 'sesskey' => sesskey()));
1379
668fc89a 1380 foreach ($records as $record) { // Might be just one for the single template
b572ce19 1381
b8b554ac 1382 // Replacing tags
3d4b223a 1383 $patterns = array();
1384 $replacement = array();
b572ce19 1385
b8b554ac 1386 // Then we generate strings to replace for normal tags
3d45b8e5 1387 foreach ($fields as $field) {
f2584d0e 1388 $patterns[]='[['.$field->field->name.']]';
d118d06a 1389 $replacement[] = highlight($search, $field->display_browse_field($record->id, $template));
3d4b223a 1390 }
b572ce19 1391
cf9ecfda
AG
1392 $canmanageentries = has_capability('mod/data:manageentries', $context);
1393
b8b554ac 1394 // Replacing special tags (##Edit##, ##Delete##, ##More##)
e357c206 1395 $patterns[]='##edit##';
1396 $patterns[]='##delete##';
97fe80ad 1397 if (data_user_can_manage_entry($record, $data, $context)) {
64452eb4 1398 $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/edit.php?d='
663640f5
DW
1399 .$data->id.'&amp;rid='.$record->id.'&amp;sesskey='.sesskey().'">' .
1400 $OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a>';
3d4b223a 1401 $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/view.php?d='
663640f5
DW
1402 .$data->id.'&amp;delete='.$record->id.'&amp;sesskey='.sesskey().'">' .
1403 $OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a>';
046dd7dc 1404 } else {
3d4b223a 1405 $replacement[] = '';
eeeb4f2a 1406 $replacement[] = '';
3d4b223a 1407 }
d53e5129 1408
1409 $moreurl = $CFG->wwwroot . '/mod/data/view.php?d=' . $data->id . '&amp;rid=' . $record->id;
32d799c6 1410 if ($search) {
1411 $moreurl .= '&amp;filter=1';
1412 }
e357c206 1413 $patterns[]='##more##';
663640f5 1414 $replacement[] = '<a href="'.$moreurl.'">' . $OUTPUT->pix_icon('t/preview', get_string('more', 'data')) . '</a>';
d53e5129 1415
e357c206 1416 $patterns[]='##moreurl##';
d53e5129 1417 $replacement[] = $moreurl;
473dd288 1418
cf9ecfda
AG
1419 $patterns[]='##delcheck##';
1420 if ($canmanageentries) {
1421 $replacement[] = html_writer::checkbox('delcheck[]', $record->id, false, '', array('class' => 'recordcheckbox'));
1422 } else {
1423 $replacement[] = '';
1424 }
1425
e357c206 1426 $patterns[]='##user##';
64452eb4 1427 $replacement[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$record->userid.
1428 '&amp;course='.$data->course.'">'.fullname($record).'</a>';
8185aeb6 1429
560237b4 1430 $patterns[] = '##userpicture##';
c910cf30 1431 $ruser = user_picture::unalias($record, null, 'userid');
80771075
JL
1432 // If the record didn't come with user data, retrieve the user from database.
1433 if (!isset($ruser->picture)) {
1434 $ruser = core_user::get_user($record->userid);
1435 }
c910cf30 1436 $replacement[] = $OUTPUT->user_picture($ruser, array('courseid' => $data->course));
560237b4 1437
8185aeb6 1438 $patterns[]='##export##';
1439
6708a1f5 1440 if (!empty($CFG->enableportfolios) && ($template == 'singletemplate' || $template == 'listtemplate')
8185aeb6 1441 && ((has_capability('mod/data:exportentry', $context)
1442 || (data_isowner($record->id) && has_capability('mod/data:exportownentry', $context))))) {
24ba58ee 1443 require_once($CFG->libdir . '/portfoliolib.php');
0d06b6fd 1444 $button = new portfolio_add_button();
37743241 1445 $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id, 'recordid' => $record->id), 'mod_data');
6be1dcae 1446 list($formats, $files) = data_portfolio_caller::formats($fields, $record);
276378e6 1447 $button->set_formats($formats);
0d06b6fd 1448 $replacement[] = $button->to_html(PORTFOLIO_ADD_ICON_LINK);
8185aeb6 1449 } else {
1450 $replacement[] = '';
1451 }
8429163d 1452
c95034f2 1453 $patterns[] = '##timeadded##';
8429163d 1454 $replacement[] = userdate($record->timecreated);
c95034f2 1455
1456 $patterns[] = '##timemodified##';
35e129c1 1457 $replacement [] = userdate($record->timemodified);
473dd288 1458
e357c206 1459 $patterns[]='##approve##';
851d3b7a 1460 if (has_capability('mod/data:approve', $context) && ($data->approval) && (!$record->approved)) {
f09040d7 1461 $approveurl = new moodle_url($jumpurl, array('approve' => $record->id));
d0372ed6 1462 $approveicon = new pix_icon('t/approve', get_string('approve', 'data'), '', array('class' => 'iconsmall'));
851d3b7a
FM
1463 $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($approveurl, $approveicon),
1464 array('class' => 'approve'));
473dd288 1465 } else {
6e0119dd 1466 $replacement[] = '';
1467 }
6403e679 1468
d0372ed6
HB
1469 $patterns[]='##disapprove##';
1470 if (has_capability('mod/data:approve', $context) && ($data->approval) && ($record->approved)) {
f09040d7 1471 $disapproveurl = new moodle_url($jumpurl, array('disapprove' => $record->id));
d0372ed6
HB
1472 $disapproveicon = new pix_icon('t/block', get_string('disapprove', 'data'), '', array('class' => 'iconsmall'));
1473 $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($disapproveurl, $disapproveicon),
1474 array('class' => 'disapprove'));
1475 } else {
1476 $replacement[] = '';
1477 }
1478
0bec64c6
HB
1479 $patterns[] = '##approvalstatus##';
1480 if (!$data->approval) {
1481 $replacement[] = '';
1482 } else if ($record->approved) {
d58f3a4d 1483 $replacement[] = get_string('approved', 'data');
0bec64c6 1484 } else {
d58f3a4d 1485 $replacement[] = get_string('notapproved', 'data');
0bec64c6
HB
1486 }
1487
e357c206 1488 $patterns[]='##comments##';
4d3d87f0 1489 if (($template == 'listtemplate') && ($data->comments)) {
e998effa
DC
1490
1491 if (!empty($CFG->usecomments)) {
36051c9e 1492 require_once($CFG->dirroot . '/comment/lib.php');
866354a9 1493 list($context, $course, $cm) = get_context_info_array($context->id);
6bdfef5d 1494 $cmt = new stdClass();
e998effa 1495 $cmt->context = $context;
866354a9
DC
1496 $cmt->course = $course;
1497 $cmt->cm = $cm;
1498 $cmt->area = 'database_entry';
e998effa
DC
1499 $cmt->itemid = $record->id;
1500 $cmt->showcount = true;
d846488e 1501 $cmt->component = 'mod_data';
e998effa 1502 $comment = new comment($cmt);
36051c9e 1503 $replacement[] = $comment->output(true);
e998effa 1504 }
4d3d87f0 1505 } else {
1506 $replacement[] = '';
1507 }
6e0119dd 1508
ca5f3e0a
AH
1509 if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
1510 $patterns[] = "##tags##";
1511 $replacement[] = $OUTPUT->tag_list(
1512 core_tag_tag::get_item_tags('mod_data', 'data_records', $record->id), '', 'data-tags');
1513 }
1514
b8b554ac 1515 // actual replacement of the tags
f2584d0e 1516 $newtext = str_ireplace($patterns, $replacement, $data->{$template});
5023c3ab 1517
b8b554ac 1518 // no more html formatting and filtering - see MDL-6635
aa3b20e9 1519 if ($return) {
1520 return $newtext;
1521 } else {
1522 echo $newtext;
b572ce19 1523
aa3b20e9 1524 // hack alert - return is always false in singletemplate anyway ;-)
1525 /**********************************
1526 * Printing Ratings Form *
1527 *********************************/
1528 if ($template == 'singletemplate') { //prints ratings options
1529 data_print_ratings($data, $record);
1530 }
b572ce19 1531
aa3b20e9 1532 /**********************************
d251b259 1533 * Printing Comments Form *
aa3b20e9 1534 *********************************/
d251b259 1535 if (($template == 'singletemplate') && ($data->comments)) {
e998effa 1536 if (!empty($CFG->usecomments)) {
36051c9e 1537 require_once($CFG->dirroot . '/comment/lib.php');
866354a9 1538 list($context, $course, $cm) = get_context_info_array($context->id);
6bdfef5d 1539 $cmt = new stdClass();
e998effa 1540 $cmt->context = $context;
866354a9
DC
1541 $cmt->course = $course;
1542 $cmt->cm = $cm;
1543 $cmt->area = 'database_entry';
e998effa
DC
1544 $cmt->itemid = $record->id;
1545 $cmt->showcount = true;
d846488e 1546 $cmt->component = 'mod_data';
e998effa 1547 $comment = new comment($cmt);
36051c9e 1548 $comment->output(false);
e998effa 1549 }
aa3b20e9 1550 }
4d3d87f0 1551 }
3d4b223a 1552 }
1553}
1554
d251b259
AD
1555/**
1556 * Return rating related permissions
2b04c41c
SH
1557 *
1558 * @param string $contextid the context id
1559 * @param string $component the component to get rating permissions for
1560 * @param string $ratingarea the rating area to get permissions for
d251b259
AD
1561 * @return array an associative array of the user's rating permissions
1562 */
2b04c41c 1563function data_rating_permissions($contextid, $component, $ratingarea) {
d197ea43 1564 $context = context::instance_by_id($contextid, MUST_EXIST);
2b04c41c 1565 if ($component != 'mod_data' || $ratingarea != 'entry') {
d251b259 1566 return null;
d251b259 1567 }
2b04c41c
SH
1568 return array(
1569 'view' => has_capability('mod/data:viewrating',$context),
1570 'viewany' => has_capability('mod/data:viewanyrating',$context),
1571 'viewall' => has_capability('mod/data:viewallratings',$context),
1572 'rate' => has_capability('mod/data:rate',$context)
1573 );
d251b259
AD
1574}
1575
aeafd436 1576/**
2c2ff8d5
AD
1577 * Validates a submitted rating
1578 * @param array $params submitted data
1579 * context => object the context in which the rated items exists [required]
1580 * itemid => int the ID of the object being rated
1581 * scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
1582 * rating => int the submitted rating
1583 * rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
1584 * aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
778361c3 1585 * @return boolean true if the rating is valid. Will throw rating_exception if not
aeafd436 1586 */
778361c3 1587function data_rating_validate($params) {
2c2ff8d5
AD
1588 global $DB, $USER;
1589
2b04c41c
SH
1590 // Check the component is mod_data
1591 if ($params['component'] != 'mod_data') {
1592 throw new rating_exception('invalidcomponent');
2c2ff8d5
AD
1593 }
1594
2b04c41c
SH
1595 // Check the ratingarea is entry (the only rating area in data module)
1596 if ($params['ratingarea'] != 'entry') {
1597 throw new rating_exception('invalidratingarea');
1598 }
1599
1600 // Check the rateduserid is not the current user .. you can't rate your own entries
1601 if ($params['rateduserid'] == $USER->id) {
1602 throw new rating_exception('nopermissiontorate');
1603 }
1604
1605 $datasql = "SELECT d.id as dataid, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
2c2ff8d5
AD
1606 FROM {data_records} r
1607 JOIN {data} d ON r.dataid = d.id
1608 WHERE r.id = :itemid";
1609 $dataparams = array('itemid'=>$params['itemid']);
1610 if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
778361c3
AD
1611 //item doesn't exist
1612 throw new rating_exception('invaliditemid');
2c2ff8d5
AD
1613 }
1614
6ac149dc
AD
1615 if ($info->scale != $params['scaleid']) {
1616 //the scale being submitted doesnt match the one in the database
1617 throw new rating_exception('invalidscaleid');
1618 }
1619
6ac149dc 1620 //check that the submitted rating is valid for the scale
5693d56c
EL
1621
1622 // lower limit
1623 if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
6ac149dc 1624 throw new rating_exception('invalidnum');
5693d56c
EL
1625 }
1626
1627 // upper limit
1628 if ($info->scale < 0) {
6ac149dc 1629 //its a custom scale
2b04c41c 1630 $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
6ac149dc
AD
1631 if ($scalerecord) {
1632 $scalearray = explode(',', $scalerecord->scale);
1633 if ($params['rating'] > count($scalearray)) {
1634 throw new rating_exception('invalidnum');
1635 }
1636 } else {
1637 throw new rating_exception('invalidscaleid');
1638 }
1639 } else if ($params['rating'] > $info->scale) {
1640 //if its numeric and submitted rating is above maximum
1641 throw new rating_exception('invalidnum');
1642 }
1643
2c2ff8d5
AD
1644 if ($info->approval && !$info->approved) {
1645 //database requires approval but this item isnt approved
778361c3 1646 throw new rating_exception('nopermissiontorate');
2c2ff8d5
AD
1647 }
1648
2b04c41c 1649 // check the item we're rating was created in the assessable time window
2c2ff8d5
AD
1650 if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
1651 if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
778361c3 1652 throw new rating_exception('notavailable');
2c2ff8d5
AD
1653 }
1654 }
1655
74df2951 1656 $course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST);
2b04c41c 1657 $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
0b3b8c30 1658 $context = context_module::instance($cm->id);
2c2ff8d5 1659
2b04c41c
SH
1660 // if the supplied context doesnt match the item's context
1661 if ($context->id != $params['context']->id) {
778361c3 1662 throw new rating_exception('invalidcontext');
2c2ff8d5
AD
1663 }
1664
1665 // Make sure groups allow this user to see the item they're rating
2b04c41c 1666 $groupid = $info->groupid;
2c2ff8d5
AD
1667 if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used
1668 if (!groups_group_exists($groupid)) { // Can't find group
778361c3 1669 throw new rating_exception('cannotfindgroup');//something is wrong
2c2ff8d5
AD
1670 }
1671
1672 if (!groups_is_member($groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
1673 // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
778361c3 1674 throw new rating_exception('notmemberofgroup');
2c2ff8d5
AD
1675 }
1676 }
1677
1678 return true;
aeafd436
AD
1679}
1680
731c2712
AA
1681/**
1682 * Can the current user see ratings for a given itemid?
1683 *
1684 * @param array $params submitted data
1685 * contextid => int contextid [required]
1686 * component => The component for this module - should always be mod_data [required]
1687 * ratingarea => object the context in which the rated items exists [required]
1688 * itemid => int the ID of the object being rated [required]
1689 * scaleid => int scale id [optional]
1690 * @return bool
1691 * @throws coding_exception
1692 * @throws rating_exception
1693 */
1694function mod_data_rating_can_see_item_ratings($params) {
1695 global $DB;
1696
1697 // Check the component is mod_data.
1698 if (!isset($params['component']) || $params['component'] != 'mod_data') {
1699 throw new rating_exception('invalidcomponent');
1700 }
1701
1702 // Check the ratingarea is entry (the only rating area in data).
1703 if (!isset($params['ratingarea']) || $params['ratingarea'] != 'entry') {
1704 throw new rating_exception('invalidratingarea');
1705 }
1706
1707 if (!isset($params['itemid'])) {
1708 throw new rating_exception('invaliditemid');
1709 }
1710
1711 $datasql = "SELECT d.id as dataid, d.course, r.groupid
1712 FROM {data_records} r
1713 JOIN {data} d ON r.dataid = d.id
1714 WHERE r.id = :itemid";
1715 $dataparams = array('itemid' => $params['itemid']);
1716 if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
1717 // Item doesn't exist.
1718 throw new rating_exception('invaliditemid');
1719 }
1720
ca3e8022
AG
1721 // User can see ratings of all participants.
1722 if ($info->groupid == 0) {
1723 return true;
1724 }
1725
731c2712
AA
1726 $course = $DB->get_record('course', array('id' => $info->course), '*', MUST_EXIST);
1727 $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
1728
1729 // Make sure groups allow this user to see the item they're rating.
1730 return groups_group_visible($info->groupid, $course, $cm);
1731}
1732
a593aeee 1733
4636bf83 1734/**
56c1ca88 1735 * function that takes in the current data, number of items per page,
1736 * a search string and prints a preference box in view.php
1737 *
1738 * This preference box prints a searchable advanced search template if
1739 * a) A template is defined
1740 * b) The advanced search checkbox is checked.
4636bf83 1741 *
1742 * @global object
1743 * @global object
56c1ca88 1744 * @param object $data
1745 * @param int $perpage
4636bf83 1746 * @param string $search
1747 * @param string $sort
1748 * @param string $order
1749 * @param array $search_array
1750 * @param int $advanced
1751 * @param string $mode
1752 * @return void
1753 */
7900ecb0 1754function data_print_preference_form($data, $perpage, $search, $sort='', $order='ASC', $search_array = '', $advanced = 0, $mode= ''){
601104f2 1755 global $CFG, $DB, $PAGE, $OUTPUT;
8429163d 1756
7900ecb0 1757 $cm = get_coursemodule_from_instance('data', $data->id);
0b3b8c30 1758 $context = context_module::instance($cm->id);
7900ecb0 1759 echo '<br /><div class="datapreferences">';
b7dc2256 1760 echo '<form id="options" action="view.php" method="get">';
7900ecb0 1761 echo '<div>';
473dd288 1762 echo '<input type="hidden" name="d" value="'.$data->id.'" />';
7900ecb0 1763 if ($mode =='asearch') {
1764 $advanced = 1;
1765 echo '<input type="hidden" name="mode" value="list" />';
1766 }
a032a460 1767 echo '<label for="pref_perpage">'.get_string('pagesize','data').'</label> ';
76a2fd82 1768 $pagesizes = array(2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,15=>15,
6e0119dd 1769 20=>20,30=>30,40=>40,50=>50,100=>100,200=>200,300=>300,400=>400,500=>500,1000=>1000);
398be7c8 1770 echo html_writer::select($pagesizes, 'perpage', $perpage, false, array('id' => 'pref_perpage', 'class' => 'custom-select'));
0d284a8b 1771
7900ecb0 1772 if ($advanced) {
0d284a8b
DM
1773 $regsearchclass = 'search_none';
1774 $advancedsearchclass = 'search_inline';
1775 } else {
1776 $regsearchclass = 'search_inline';
1777 $advancedsearchclass = 'search_none';
7900ecb0 1778 }
e85fda95 1779 echo '<div id="reg_search" class="' . $regsearchclass . ' form-inline" >&nbsp;&nbsp;&nbsp;';
398be7c8
DW
1780 echo '<label for="pref_search">' . get_string('search') . '</label> <input type="text" ' .
1781 'class="form-control" size="16" name="search" id= "pref_search" value="' . s($search) . '" /></div>';
a032a460 1782 echo '&nbsp;&nbsp;&nbsp;<label for="pref_sortby">'.get_string('sortby').'</label> ';
b8b554ac 1783 // foreach field, print the option
e85fda95 1784 echo '<select name="sort" id="pref_sortby" class="custom-select m-r-1">';
9c00b5d7 1785 if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'name')) {
3239b010 1786 echo '<optgroup label="'.get_string('fields', 'data').'">';
1787 foreach ($fields as $field) {
1788 if ($field->id == $sort) {
1789 echo '<option value="'.$field->id.'" selected="selected">'.$field->name.'</option>';
1790 } else {
1791 echo '<option value="'.$field->id.'">'.$field->name.'</option>';
1792 }
1793 }
1794 echo '</optgroup>';
714bec74 1795 }
3239b010 1796 $options = array();
1797 $options[DATA_TIMEADDED] = get_string('timeadded', 'data');
1798 $options[DATA_TIMEMODIFIED] = get_string('timemodified', 'data');
1799 $options[DATA_FIRSTNAME] = get_string('authorfirstname', 'data');
1800 $options[DATA_LASTNAME] = get_string('authorlastname', 'data');
bb5740f4 1801 if ($data->approval and has_capability('mod/data:approve', $context)) {
1802 $options[DATA_APPROVED] = get_string('approved', 'data');
1803 }
3239b010 1804 echo '<optgroup label="'.get_string('other', 'data').'">';
714bec74 1805 foreach ($options as $key => $name) {
1806 if ($key == $sort) {
1807 echo '<option value="'.$key.'" selected="selected">'.$name.'</option>';
cf3e199b 1808 } else {
714bec74 1809 echo '<option value="'.$key.'">'.$name.'</option>';
cf3e199b 1810 }
1811 }
3239b010 1812 echo '</optgroup>';
cf3e199b 1813 echo '</select>';
a032a460 1814 echo '<label for="pref_order" class="accesshide">'.get_string('order').'</label>';
e85fda95 1815 echo '<select id="pref_order" name="order" class="custom-select m-r-1">';
c8505cac 1816 if ($order == 'ASC') {
af25f45e 1817 echo '<option value="ASC" selected="selected">'.get_string('ascending','data').'</option>';
cf3e199b 1818 } else {
1819 echo '<option value="ASC">'.get_string('ascending','data').'</option>';
1820 }
c8505cac 1821 if ($order == 'DESC') {
af25f45e 1822 echo '<option value="DESC" selected="selected">'.get_string('descending','data').'</option>';
cf3e199b 1823 } else {
1824 echo '<option value="DESC">'.get_string('descending','data').'</option>';
1825 }
af25f45e 1826 echo '</select>';
86638489 1827
7900ecb0 1828 if ($advanced) {
1829 $checked = ' checked="checked" ';
1830 }
1831 else {
1832 $checked = '';
1833 }
9dec75db 1834 $PAGE->requires->js('/mod/data/data.js');
de8ff581 1835 echo '&nbsp;<input type="hidden" name="advanced" value="0" />';
d53e5129 1836 echo '&nbsp;<input type="hidden" name="filter" value="1" />';
398be7c8
DW
1837 echo '&nbsp;<input type="checkbox" id="advancedcheckbox" name="advanced" value="1" ' . $checked . ' ' .
1838 'onchange="showHideAdvSearch(this.checked);" class="m-x-1" />' .
1839 '<label for="advancedcheckbox">' . get_string('advancedsearch', 'data') . '</label>';
1840 echo '&nbsp;<input type="submit" class="btn btn-secondary" value="' . get_string('savesettings', 'data') . '" />';
8429163d 1841
7900ecb0 1842 echo '<br />';
0d284a8b 1843 echo '<div class="' . $advancedsearchclass . '" id="data_adv_form">';
7900ecb0 1844 echo '<table class="boxaligncenter">';
8429163d 1845
7900ecb0 1846 // print ASC or DESC
1847 echo '<tr><td colspan="2">&nbsp;</td></tr>';
1848 $i = 0;
1849
1850 // Determine if we are printing all fields for advanced search, or the template for advanced search
1851 // If a template is not defined, use the deafault template and display all fields.
1852 if(empty($data->asearchtemplate)) {
1853 data_generate_default_template($data, 'asearchtemplate');
1854 }
1855
2c216a6c
MN
1856 static $fields = array();
1857 static $dataid = null;
b572ce19 1858
7900ecb0 1859 if (empty($dataid)) {
1860 $dataid = $data->id;
1861 } else if ($dataid != $data->id) {
2c216a6c 1862 $fields = array();
7900ecb0 1863 }
1864
1865 if (empty($fields)) {
9c00b5d7 1866 $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id));
7900ecb0 1867 foreach ($fieldrecords as $fieldrecord) {
1868 $fields[]= data_get_field($fieldrecord, $data);
1869 }
7900ecb0 1870 }
1871
b8b554ac 1872 // Replacing tags
7900ecb0 1873 $patterns = array();
1874 $replacement = array();
1875
b8b554ac 1876 // Then we generate strings to replace for normal tags
7900ecb0 1877 foreach ($fields as $field) {
98f67312 1878 $fieldname = $field->field->name;
1879 $fieldname = preg_quote($fieldname, '/');
1880 $patterns[] = "/\[\[$fieldname\]\]/i";
714bec74 1881 $searchfield = data_get_field_from_id($field->field->id, $data);
7900ecb0 1882 if (!empty($search_array[$field->field->id]->data)) {
1883 $replacement[] = $searchfield->display_search_field($search_array[$field->field->id]->data);
1884 } else {
1885 $replacement[] = $searchfield->display_search_field();
1886 }
1887 }
714bec74 1888 $fn = !empty($search_array[DATA_FIRSTNAME]->data) ? $search_array[DATA_FIRSTNAME]->data : '';
1889 $ln = !empty($search_array[DATA_LASTNAME]->data) ? $search_array[DATA_LASTNAME]->data : '';
1890 $patterns[] = '/##firstname##/';
398be7c8
DW
1891 $replacement[] = '<label class="accesshide" for="u_fn">' . get_string('authorfirstname', 'data') . '</label>' .
1892 '<input type="text" class="form-control" size="16" id="u_fn" name="u_fn" value="' . s($fn) . '" />';
714bec74 1893 $patterns[] = '/##lastname##/';
398be7c8
DW
1894 $replacement[] = '<label class="accesshide" for="u_ln">' . get_string('authorlastname', 'data') . '</label>' .
1895 '<input type="text" class="form-control" size="16" id="u_ln" name="u_ln" value="' . s($ln) . '" />';
714bec74 1896
668fc89a 1897 // actual replacement of the tags
7900ecb0 1898 $newtext = preg_replace($patterns, $replacement, $data->asearchtemplate);
b572ce19 1899
39790bd8 1900 $options = new stdClass();
7900ecb0 1901 $options->para=false;
1902 $options->noclean=true;
1903 echo '<tr><td>';
1904 echo format_text($newtext, FORMAT_HTML, $options);
1905 echo '</td></tr>';
b572ce19 1906
398be7c8
DW
1907 echo '<tr><td colspan="4"><br/>' .
1908 '<input type="submit" class="btn btn-primary m-r-1" value="' . get_string('savesettings', 'data') . '" />' .
1909 '<input type="submit" class="btn btn-secondary" name="resetadv" value="' . get_string('resetsettings', 'data') . '" />' .
1910 '</td></tr>';
7900ecb0 1911 echo '</table>';
c8505cac 1912 echo '</div>';
7900ecb0 1913 echo '</div>';
1914 echo '</form>';
b8b554ac 1915 echo '</div>';
3d4b223a 1916}
5f5bcda8 1917
4636bf83 1918/**
1919 * @global object
1920 * @global object
1921 * @param object $data
1922 * @param object $record
1923 * @return void Output echo'd
1924 */
4d3d87f0 1925function data_print_ratings($data, $record) {
56c1ca88 1926 global $OUTPUT;
2b04c41c 1927 if (!empty($record->rating)){
d251b259 1928 echo $OUTPUT->render($record->rating);
4d3d87f0 1929 }
4d3d87f0 1930}
1931
4636bf83 1932/**
b2b4ec30
RT
1933 * List the actions that correspond to a view of this module.
1934 * This is used by the participation report.
1935 *
1936 * Note: This is not used by new logging system. Event with
1937 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
1938 * be considered as view action.
4636bf83 1939 *
1940 * @return array
1941 */
4c03f920 1942function data_get_view_actions() {
1943 return array('view');
1944}
1945
4636bf83 1946/**
b2b4ec30
RT
1947 * List the actions that correspond to a post of this module.
1948 * This is used by the participation report.
1949 *
1950 * Note: This is not used by new logging system. Event with
1951 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
1952 * will be considered as post action.
1953 *
4636bf83 1954 * @return array
1955 */
4c03f920 1956function data_get_post_actions() {
1957 return array('add','update','record delete');
1958}
1959
4636bf83 1960/**
4636bf83 1961 * @param string $name
1962 * @param int $dataid
1963 * @param int $fieldid
1964 * @return bool
1965 */
e9da9f5b
PS
1966function data_fieldname_exists($name, $dataid, $fieldid = 0) {
1967 global $DB;
0997e51a 1968
e9da9f5b
PS
1969 if (!is_numeric($name)) {
1970 $like = $DB->sql_like('df.name', ':name', false);
fdeb3e1f 1971 } else {
e9da9f5b 1972 $like = "df.name = :name";
fdeb3e1f 1973 }
e9da9f5b 1974 $params = array('name'=>$name);
6403e679 1975 if ($fieldid) {
e9da9f5b
PS
1976 $params['dataid'] = $dataid;
1977 $params['fieldid1'] = $fieldid;
1978 $params['fieldid2'] = $fieldid;
9c00b5d7 1979 return $DB->record_exists_sql("SELECT * FROM {data_fields} df
e9da9f5b
PS
1980 WHERE $like AND df.dataid = :dataid
1981 AND ((df.id < :fieldid1) OR (df.id > :fieldid2))", $params);
0997e51a 1982 } else {
e9da9f5b 1983 $params['dataid'] = $dataid;
9c00b5d7 1984 return $DB->record_exists_sql("SELECT * FROM {data_fields} df
e9da9f5b 1985 WHERE $like AND df.dataid = :dataid", $params);
0997e51a 1986 }
1987}
1988
4636bf83 1989/**
1990 * @param array $fieldinput
1991 */
0997e51a 1992function data_convert_arrays_to_strings(&$fieldinput) {
1993 foreach ($fieldinput as $key => $val) {
1994 if (is_array($val)) {
1995 $str = '';
1996 foreach ($val as $inner) {
1997 $str .= $inner . ',';
1998 }
1999 $str = substr($str, 0, -1);
b572ce19 2000
0997e51a 2001 $fieldinput->$key = $str;
2002 }
2003 }
2004}
2005
901dd2fb 2006
7f258664 2007/**
2008 * Converts a database (module instance) to use the Roles System
4636bf83 2009 *
2010 * @global object
2011 * @global object
2012 * @uses CONTEXT_MODULE
2013 * @uses CAP_PREVENT
2014 * @uses CAP_ALLOW
2015 * @param object $data a data object with the same attributes as a record
2016 * from the data database table
2017 * @param int $datamodid the id of the data module, from the modules table
4f0c2d00
PS
2018 * @param array $teacherroles array of roles that have archetype teacher
2019 * @param array $studentroles array of roles that have archetype student
2020 * @param array $guestroles array of roles that have archetype guest
4636bf83 2021 * @param int $cmid the course_module id for this data instance
2022 * @return boolean data module was converted or not
7f258664 2023 */
2024function data_convert_to_roles($data, $teacherroles=array(), $studentroles=array(), $cmid=NULL) {
4102b449 2025 global $CFG, $DB, $OUTPUT;
6403e679 2026
7f258664 2027 if (!isset($data->participants) && !isset($data->assesspublic)
2028 && !isset($data->groupmode)) {
2029 // We assume that this database has already been converted to use the
2030 // Roles System. above fields get dropped the data module has been
2031 // upgraded to use Roles.
2032 return false;
2033 }
b572ce19 2034
7f258664 2035 if (empty($cmid)) {
2036 // We were not given the course_module id. Try to find it.
741c4d0b 2037 if (!$cm = get_coursemodule_from_instance('data', $data->id)) {
4102b449 2038 echo $OUTPUT->notification('Could not get the course module for the data');
7f258664 2039 return false;
2040 } else {
2041 $cmid = $cm->id;
2042 }
2043 }
0b3b8c30 2044 $context = context_module::instance($cmid);
6403e679 2045
b572ce19 2046
7f258664 2047 // $data->participants:
2048 // 1 - Only teachers can add entries
2049 // 3 - Teachers and students can add entries
2050 switch ($data->participants) {
2051 case 1:
2052 foreach ($studentroles as $studentrole) {
2053 assign_capability('mod/data:writeentry', CAP_PREVENT, $studentrole->id, $context->id);
2054 }
2055 foreach ($teacherroles as $teacherrole) {
2056 assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
2057 }
2058 break;
2059 case 3:
2060 foreach ($studentroles as $studentrole) {
2061 assign_capability('mod/data:writeentry', CAP_ALLOW, $studentrole->id, $context->id);
2062 }
2063 foreach ($teacherroles as $teacherrole) {
2064 assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
2065 }
2066 break;
2067 }
6403e679 2068
7f258664 2069 // $data->assessed:
2070 // 2 - Only teachers can rate posts
2071 // 1 - Everyone can rate posts
2072 // 0 - No one can rate posts
2073 switch ($data->assessed) {
2074 case 0:
2075 foreach ($studentroles as $studentrole) {
2076 assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
2077 }
2078 foreach ($teacherroles as $teacherrole) {
2079 assign_capability('mod/data:rate', CAP_PREVENT, $teacherrole->id, $context->id);
2080 }
2081 break;
2082 case 1:
2083 foreach ($studentroles as $studentrole) {
2084 assign_capability('mod/data:rate', CAP_ALLOW, $studentrole->id, $context->id);
2085 }
2086 foreach ($teacherroles as $teacherrole) {
2087 assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
2088 }
2089 break;
2090 case 2:
2091 foreach ($studentroles as $studentrole) {
2092 assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
2093 }
2094 foreach ($teacherroles as $teacherrole) {
2095 assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
2096 }
2097 break;
2098 }
6403e679 2099
7f258664 2100 // $data->assesspublic:
2101 // 0 - Students can only see their own ratings
2102 // 1 - Students can see everyone's ratings
2103 switch ($data->assesspublic) {
2104 case 0:
2105 foreach ($studentroles as $studentrole) {
2106 assign_capability('mod/data:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
2107 }
2108 foreach ($teacherroles as $teacherrole) {
2109 assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
2110 }
2111 break;
2112 case 1:
2113 foreach ($studentroles as $studentrole) {
2114 assign_capability('mod/data:viewrating', CAP_ALLOW, $studentrole->id, $context->id);
2115 }
2116 foreach ($teacherroles as $teacherrole) {
2117 assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
2118 }
2119 break;
2120 }
2121
2122 if (empty($cm)) {
9c00b5d7 2123 $cm = $DB->get_record('course_modules', array('id'=>$cmid));
7f258664 2124 }
6403e679 2125
7f258664 2126 switch ($cm->groupmode) {
055f2185 2127 case NOGROUPS:
7f258664 2128 break;
055f2185 2129 case SEPARATEGROUPS:
7f258664 2130 foreach ($studentroles as $studentrole) {
2131 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
2132 }
2133 foreach ($teacherroles as $teacherrole) {
2134 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
2135 }
2136 break;
055f2185 2137 case VISIBLEGROUPS:
7f258664 2138 foreach ($studentroles as $studentrole) {
2139 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
2140 }
2141 foreach ($teacherroles as $teacherrole) {
2142 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
2143 }
2144 break;
2145 }
2146 return true;
2147}
2148
4636bf83 2149/**
8303eb84 2150 * Returns the best name to show for a preset
4636bf83 2151 *
2152 * @param string $shortname
2153 * @param string $path
2154 * @return string
8303eb84 2155 */
2156function data_preset_name($shortname, $path) {
2157
668fc89a 2158 // We are looking inside the preset itself as a first choice, but also in normal data directory
30d76014 2159 $string = get_string('modulename', 'datapreset_'.$shortname);
8303eb84 2160
2161 if (substr($string, 0, 1) == '[') {
2162 return $shortname;
2163 } else {
2164 return $string;
2165 }
2166}
2167
4636bf83 2168/**
8aff1574 2169 * Returns an array of all the available presets.
4636bf83 2170 *
4636bf83 2171 * @return array
8303eb84 2172 */
2173function data_get_available_presets($context) {
2174 global $CFG, $USER;
b572ce19 2175
8303eb84 2176 $presets = array();
b572ce19 2177
8aff1574 2178 // First load the ratings sub plugins that exist within the modules preset dir
bd3b3bba 2179 if ($dirs = core_component::get_plugin_list('datapreset')) {
a9cda870 2180 foreach ($dirs as $dir=>$fulldir) {
8303eb84 2181 if (is_directory_a_preset($fulldir)) {
39790bd8 2182 $preset = new stdClass();
8303eb84 2183 $preset->path = $fulldir;
2184 $preset->userid = 0;
2185 $preset->shortname = $dir;
2186 $preset->name = data_preset_name($dir, $fulldir);
2187 if (file_exists($fulldir.'/screenshot.jpg')) {
2188 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.jpg';
2189 } else if (file_exists($fulldir.'/screenshot.png')) {
2190 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.png';
2191 } else if (file_exists($fulldir.'/screenshot.gif')) {
2192 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.gif';
2193 }
2194 $presets[] = $preset;
2195 }
2196 }
2197 }
8aff1574
SH
2198 // Now add to that the site presets that people have saved
2199 $presets = data_get_available_site_presets($context, $presets);
2200 return $presets;
2201}
8303eb84 2202
8aff1574
SH
2203/**
2204 * Gets an array of all of the presets that users have saved to the site.
2205 *
2206 * @param stdClass $context The context that we are looking from.
2207 * @param array $presets
2208 * @return array An array of presets
2209 */
2210function data_get_available_site_presets($context, array $presets=array()) {
810860d2
PS
2211 global $USER;
2212
8aff1574
SH
2213 $fs = get_file_storage();
2214 $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
2215 $canviewall = has_capability('mod/data:viewalluserpresets', $context);
2216 if (empty($files)) {
2217 return $presets;
2218 }
2219 foreach ($files as $file) {
2220 if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory() || (!$canviewall && $file->get_userid() != $USER->id)) {
2221 continue;
2222 }
2223 $preset = new stdClass;
2224 $preset->path = $file->get_filepath();
2225 $preset->name = trim($preset->path, '/');
2226 $preset->shortname = $preset->name;
2227 $preset->userid = $file->get_userid();
2228 $preset->id = $file->get_id();
2229 $preset->storedfile = $file;
2230 $presets[] = $preset;
2231 }
2232 return $presets;
2233}
2234
2235/**
2236 * Deletes a saved preset.
2237 *
2238 * @param string $name
2239 * @return bool
2240 */
2241function data_delete_site_preset($name) {
2242 $fs = get_file_storage();
2243
2244 $files = $fs->get_directory_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/');
2245 if (!empty($files)) {
2246 foreach ($files as $file) {
2247 $file->delete();
8303eb84 2248 }
2249 }
b572ce19 2250
8aff1574
SH
2251 $dir = $fs->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/', '.');
2252 if (!empty($dir)) {
2253 $dir->delete();
2254 }
2255 return true;
8303eb84 2256}
2257
4636bf83 2258/**
8aff1574
SH
2259 * Prints the heads for a page
2260 *
2261 * @param stdClass $course
2262 * @param stdClass $cm
2263 * @param stdClass $data
4636bf83 2264 * @param string $currenttab
2265 */
8303eb84 2266function data_print_header($course, $cm, $data, $currenttab='') {
b572ce19 2267
b0ff558c 2268 global $CFG, $displaynoticegood, $displaynoticebad, $OUTPUT, $PAGE;
b572ce19 2269
b0ff558c 2270 $PAGE->set_title($data->name);
b0ff558c 2271 echo $OUTPUT->header();
66e2b9f8
AD
2272 echo $OUTPUT->heading(format_string($data->name), 2);
2273 echo $OUTPUT->box(format_module_intro('data', $data, $cm->id), 'generalbox', 'intro');
8303eb84 2274
66e2b9f8 2275 // Groups needed for Add entry tab
055f2185 2276 $currentgroup = groups_get_activity_group($cm);
5d59cbe9 2277 $groupmode = groups_get_activity_groupmode($cm);
b572ce19 2278
668fc89a 2279 // Print the tabs
b572ce19 2280
8303eb84 2281 if ($currenttab) {
138e480e 2282 include('tabs.php');
8303eb84 2283 }
b572ce19 2284
b8b554ac 2285 // Print any notices
b572ce19 2286
8303eb84 2287 if (!empty($displaynoticegood)) {
4102b449 2288 echo $OUTPUT->notification($displaynoticegood, 'notifysuccess'); // good (usually green)
8303eb84 2289 } else if (!empty($displaynoticebad)) {
4102b449 2290 echo $OUTPUT->notification($displaynoticebad); // bad (usuually red)
8303eb84 2291 }
2292}
7f258664 2293
4636bf83 2294/**
0393a8b7
PS
2295 * Can user add more entries?
2296 *
4636bf83 2297 * @param object $data
2298 * @param mixed $currentgroup
2299 * @param int $groupmode
0393a8b7 2300 * @param stdClass $context
4636bf83 2301 * @return bool
2302 */
e4e96115 2303function data_user_can_add_entry($data, $currentgroup, $groupmode, $context = null) {
cca1547e 2304 global $USER;
b572ce19 2305
e4e96115 2306 if (empty($context)) {
0393a8b7 2307 $cm = get_coursemodule_from_instance('data', $data->id, 0, false, MUST_EXIST);
0b3b8c30 2308 $context = context_module::instance($cm->id);
e4e96115
RW
2309 }
2310
0393a8b7
PS
2311 if (has_capability('mod/data:manageentries', $context)) {
2312 // no entry limits apply if user can manage
e4e96115 2313
0393a8b7 2314 } else if (!has_capability('mod/data:writeentry', $context)) {
e4e96115 2315 return false;
b572ce19 2316
0393a8b7 2317 } else if (data_atmaxentries($data)) {
cca1547e 2318 return false;
e8027a40
ARN
2319 } else if (data_in_readonly_period($data)) {
2320 // Check whether we're in a read-only period
2742ffe7
AD
2321 return false;
2322 }
2323
04366d79 2324 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
2325 return true;
2326 }
2327
cca1547e 2328 if ($currentgroup) {
2c386f82 2329 return groups_is_member($currentgroup);
cca1547e 2330 } else {
2331 //else it might be group 0 in visible mode
2332 if ($groupmode == VISIBLEGROUPS){
cca1547e 2333 return true;
04366d79 2334 } else {
2335 return false;
cca1547e 2336 }
2337 }
2338}
2339
97fe80ad
JB
2340/**
2341 * Check whether the current user is allowed to manage the given record considering manageentries capability,
2342 * data_in_readonly_period() result, ownership (determined by data_isowner()) and manageapproved setting.
2343 * @param mixed $record record object or id
2344 * @param object $data data object
2345 * @param object $context context object
2346 * @return bool returns true if the user is allowd to edit the entry, false otherwise
2347 */
2348function data_user_can_manage_entry($record, $data, $context) {
2349 global $DB;
2350
2351 if (has_capability('mod/data:manageentries', $context)) {
2352 return true;
2353 }
2354
2355 // Check whether this activity is read-only at present.
2356 $readonly = data_in_readonly_period($data);
2357
2358 if (!$readonly) {
2359 // Get record object from db if just id given like in data_isowner.
2360 // ...done before calling data_isowner() to avoid querying db twice.
2361 if (!is_object($record)) {
2362 if (!$record = $DB->get_record('data_records', array('id' => $record))) {
2363 return false;
2364 }
2365 }
2366 if (data_isowner($record)) {
2367 if ($data->approval && $record->approved) {
2368 return $data->manageapproved == 1;
2369 } else {
2370 return true;
2371 }
2372 }
2373 }
2374
2375 return false;
2376}
2377
e8027a40
ARN
2378/**
2379 * Check whether the specified database activity is currently in a read-only period
2380 *
2381 * @param object $data
2382 * @return bool returns true if the time fields in $data indicate a read-only period; false otherwise
2383 */
2384function data_in_readonly_period($data) {
2385 $now = time();
2386 if (!$data->timeviewfrom && !$data->timeviewto) {
2387 return false;
2388 } else if (($data->timeviewfrom && $now < $data->timeviewfrom) || ($data->timeviewto && $now > $data->timeviewto)) {
2389 return false;
2390 }
2391 return true;
2392}
50194545 2393
4636bf83 2394/**
2395 * @return bool
2396 */
50194545 2397function is_directory_a_preset($directory) {
2398 $directory = rtrim($directory, '/\\') . '/';
2dc6be3f 2399 $status = file_exists($directory.'singletemplate.html') &&
2400 file_exists($directory.'listtemplate.html') &&
2401 file_exists($directory.'listtemplateheader.html') &&
2402 file_exists($directory.'listtemplatefooter.html') &&
2403 file_exists($directory.'addtemplate.html') &&
2404 file_exists($directory.'rsstemplate.html') &&
2405 file_exists($directory.'rsstitletemplate.html') &&
2406 file_exists($directory.'csstemplate.css') &&
2407 file_exists($directory.'jstemplate.js') &&
2408 file_exists($directory.'preset.xml');
b572ce19 2409
2dc6be3f 2410 return $status;
50194545 2411}
2412
4636bf83 2413/**
cba87c36 2414 * Abstract class used for data preset importers
4636bf83 2415 */
cba87c36 2416abstract class data_preset_importer {
b572ce19 2417
cba87c36
SH
2418 protected $course;
2419 protected $cm;
2420 protected $module;
2421 protected $directory;
50194545 2422
4636bf83 2423 /**
cba87c36
SH
2424 * Constructor
2425 *
2426 * @param stdClass $course
2427 * @param stdClass $cm
2428 * @param stdClass $module
2429 * @param string $directory
4636bf83 2430 */
cba87c36 2431 public function __construct($course, $cm, $module, $directory) {
50194545 2432 $this->course = $course;
2433 $this->cm = $cm;
cba87c36
SH
2434 $this->module = $module;
2435 $this->directory = $directory;
2436 }
2437
2438 /**
2439 * Returns the name of the directory the preset is located in
2440 * @return string
2441 */
2442 public function get_directory() {
2443 return basename($this->directory);
50194545 2444 }
263819c8
AD
2445
2446 /**
2447 * Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage
2448 * @param file_storage $filestorage. should be null if using a conventional directory
2449 * @param stored_file $fileobj the directory to look in. null if using a conventional directory
2450 * @param string $dir the directory to look in. null if using the Moodle file storage
2451 * @param string $filename the name of the file we want
c600a85c 2452 * @return string the contents of the file or null if the file doesn't exist.
263819c8
AD
2453 */
2454 public function data_preset_get_file_contents(&$filestorage, &$fileobj, $dir, $filename) {
2455 if(empty($filestorage) || empty($fileobj)) {
2456 if (substr($dir, -1)!='/') {
2457 $dir .= '/';
2458 }
c600a85c
AG
2459 if (file_exists($dir.$filename)) {
2460 return file_get_contents($dir.$filename);
2461 } else {
2462 return null;
2463 }
263819c8 2464 } else {
c600a85c
AG
2465 if ($filestorage->file_exists(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename)) {
2466 $file = $filestorage->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename);
2467 return $file->get_content();
2468 } else {
2469 return null;
2470 }
263819c8
AD
2471 }
2472
2473 }
4636bf83 2474 /**
cba87c36
SH
2475 * Gets the preset settings
2476 * @global moodle_database $DB
2477 * @return stdClass
4636bf83 2478 */
cba87c36
SH
2479 public function get_preset_settings() {
2480 global $DB;
50194545 2481
263819c8 2482 $fs = $fileobj = null;
cba87c36 2483 if (!is_directory_a_preset($this->directory)) {
263819c8 2484 //maybe the user requested a preset stored in the Moodle file storage
375053ed 2485
263819c8
AD
2486 $fs = get_file_storage();
2487 $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
2488
720f37fd 2489 //preset name to find will be the final element of the directory
c600a85c
AG
2490 $explodeddirectory = explode('/', $this->directory);
2491 $presettofind = end($explodeddirectory);
720f37fd
AD
2492
2493 //now go through the available files available and see if we can find it
263819c8
AD
2494 foreach ($files as $file) {
2495 if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory()) {
2496 continue;
2497 }
2498 $presetname = trim($file->get_filepath(), '/');
720f37fd 2499 if ($presetname==$presettofind) {
263819c8
AD
2500 $this->directory = $presetname;
2501 $fileobj = $file;
2502 }
2503 }
2504
2505 if (empty($fileobj)) {
2506 print_error('invalidpreset', 'data', '', $this->directory);
2507 }
50194545 2508 }
2509
cba87c36
SH
2510 $allowed_settings = array(
2511 'intro',
2512 'comments',
2513 'requiredentries',
2514 'requiredentriestoview',
2515 'maxentries',
2516 'rssarticles',
2517 'approval',
2518 'defaultsortdir',
2519 'defaultsort');
2520
2521 $result = new stdClass;
2522 $result->settings = new stdClass;
2523 $result->importfields = array();
2524 $result->currentfields = $DB->get_records('data_fields', array('dataid'=>$this->module->id));
2525 if (!$result->currentfields) {
2526 $result->currentfields = array();
2527 }
2528
2529
50194545 2530 /* Grab XML */
263819c8 2531 $presetxml = $this->data_preset_get_file_contents($fs, $fileobj, $this->directory,'preset.xml');
143c1eb9 2532 $parsedxml = xmlize($presetxml, 0);
50194545 2533
2534 /* First, do settings. Put in user friendly array. */
2535 $settingsarray = $parsedxml['preset']['#']['settings'][0]['#'];
cba87c36 2536 $result->settings = new StdClass();
50194545 2537 foreach ($settingsarray as $setting => $value) {
cba87c36 2538 if (!is_array($value) || !in_array($setting, $allowed_settings)) {
eaa30818 2539 // unsupported setting
2540 continue;
2541 }
cba87c36 2542 $result->settings->$setting = $value[0]['#'];
50194545 2543 }
2544
2545 /* Now work out fields to user friendly array */
2546 $fieldsarray = $parsedxml['preset']['#']['field'];
50194545 2547 foreach ($fieldsarray as $field) {
02007b01 2548 if (!is_array($field)) {
2549 continue;
2550 }
50194545 2551 $f = new StdClass();
2552 foreach ($field['#'] as $param => $value) {
02007b01 2553 if (!is_array($value)) {
2554 continue;
2555 }
9c00b5d7 2556 $f->$param = $value[0]['#'];
50194545 2557 }
cba87c36 2558 $f->dataid = $this->module->id;
50194545 2559 $f->type = clean_param($f->type, PARAM_ALPHA);
cba87c36 2560 $result->importfields[] = $f;
50194545 2561 }
50194545 2562 /* Now add the HTML templates to the settings array so we can update d */
263819c8
AD
2563 $result->settings->singletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"singletemplate.html");
2564 $result->settings->listtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplate.html");
2565 $result->settings->listtemplateheader = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplateheader.html");
2566 $result->settings->listtemplatefooter = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplatefooter.html");
2567 $result->settings->addtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"addtemplate.html");
2568 $result->settings->rsstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstemplate.html");
2569 $result->settings->rsstitletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstitletemplate.html");
2570 $result->settings->csstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"csstemplate.css");
2571 $result->settings->jstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"jstemplate.js");
c600a85c 2572 $result->settings->asearchtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"asearchtemplate.html");
50194545 2573
cba87c36 2574 $result->settings->instance = $this->module->id;
cba87c36 2575 return $result;
50194545 2576 }
2577
4636bf83 2578 /**
cba87c36 2579 * Import the preset into the given database module
4636bf83 2580 * @return bool
2581 */
cba87c36 2582 function import($overwritesettings) {
fe9d5f7e 2583 global $DB, $CFG;
50194545 2584
cba87c36
SH
2585 $params = $this->get_preset_settings();
2586 $settings = $params->settings;
2587 $newfields = $params->importfields;
2588 $currentfields = $params->currentfields;
50194545 2589 $preservedfields = array();
b572ce19 2590
50194545 2591 /* Maps fields and makes new ones */
2592 if (!empty($newfields)) {
2593 /* We require an injective mapping, and need to know what to protect */
2594 foreach ($newfields as $nid => $newfield) {
2595 $cid = optional_param("field_$nid", -1, PARAM_INT);
cba87c36
SH
2596 if ($cid == -1) {
2597 continue;
2598 }
29c1bb15 2599 if (array_key_exists($cid, $preservedfields)){
2600 print_error('notinjectivemap', 'data');
8429163d 2601 }
50194545 2602 else $preservedfields[$cid] = true;
2603 }
b572ce19 2604
50194545 2605 foreach ($newfields as $nid => $newfield) {
2606 $cid = optional_param("field_$nid", -1, PARAM_INT);
b572ce19 2607
50194545 2608 /* A mapping. Just need to change field params. Data kept. */
2609 if ($cid != -1 and isset($currentfields[$cid])) {
cba87c36 2610 $fieldobject = data_get_field_from_id($currentfields[$cid]->id, $this->module);
50194545 2611 foreach ($newfield as $param => $value) {
2612 if ($param != "id") {
2613 $fieldobject->field->$param = $value;
2614 }
2615 }
2616 unset($fieldobject->field->similarfield);
2617 $fieldobject->update_field();
2618 unset($fieldobject);
cba87c36
SH
2619 } else {
2620 /* Make a new field */
50194545 2621 include_once("field/$newfield->type/field.class.php");
b572ce19 2622
50194545 2623 if (!isset($newfield->description)) {
2624 $newfield->description = '';
2625 }
2626 $classname = 'data_field_'.$newfield->type;
cba87c36 2627 $fieldclass = new $classname($newfield, $this->module);
50194545 2628 $fieldclass->insert_field();
2629 unset($fieldclass);
2630 }
2631 }
2632 }
2633
2634 /* Get rid of all old unused data */
2635 if (!empty($preservedfields)) {
2636 foreach ($currentfields as $cid => $currentfield) {
2637 if (!array_key_exists($cid, $preservedfields)) {
2638 /* Data not used anymore so wipe! */
2639 print "Deleting field $currentfield->name<br />";
b572ce19 2640
50194545 2641 $id = $currentfield->id;
2642 //Why delete existing data records and related comments/ratings??
9c00b5d7 2643 $DB->delete_records('data_content', array('fieldid'=>$id));
2644 $DB->delete_records('data_fields', array('id'=>$id));
50194545 2645 }
2646 }
2647 }
2648
cba87c36 2649 // handle special settings here
eaa30818 2650 if (!empty($settings->defaultsort)) {
2651 if (is_numeric($settings->defaultsort)) {
668fc89a 2652 // old broken value
eaa30818 2653 $settings->defaultsort = 0;
2654 } else {
cba87c36 2655 $settings->defaultsort = (int)$DB->get_field('data_fields', 'id', array('dataid'=>$this->module->id, 'name'=>$settings->defaultsort));
eaa30818 2656 }
2657 } else {
2658 $settings->defaultsort = 0;
2659 }
2660
2661 // do we want to overwrite all current database settings?
8528bf26 2662 if ($overwritesettings) {
eaa30818 2663 // all supported settings
8528bf26 2664 $overwrite = array_keys((array)$settings);
2665 } else {
eaa30818 2666 // only templates and sorting
8528bf26 2667 $overwrite = array('singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
2668 'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate',
eaa30818 2669 'asearchtemplate', 'defaultsortdir', 'defaultsort');
8528bf26 2670 }
2671
eaa30818 2672 // now overwrite current data settings
cba87c36 2673 foreach ($this->module as $prop=>$unused) {
8528bf26 2674 if (in_array($prop, $overwrite)) {
cba87c36 2675 $this->module->$prop = $settings->$prop;
13534ef7
ML
2676 }
2677 }
db0f2a44 2678
cba87c36 2679 data_update_instance($this->module);
50194545 2680
cba87c36
SH
2681 return $this->cleanup();
2682 }
b572ce19 2683
cba87c36
SH
2684 /**
2685 * Any clean up routines should go here
2686 * @return bool
2687 */
2688 public function cleanup() {
50194545 2689 return true;
2690 }
2691}
2692
cba87c36
SH
2693/**
2694 * Data preset importer for uploaded presets
2695 */
2696class data_preset_upload_importer extends data_preset_importer {
2697 public function __construct($course, $cm, $module, $filepath) {
2698 global $USER;
2699 if (is_file($filepath)) {
2700 $fp = get_file_packer();
2701 if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) {
2702 fulldelete($filepath);
2703 }
2704 $filepath .= '_extracted';
2705 }
2706 parent::__construct($course, $cm, $module, $filepath);
2707 }
2708 public function cleanup() {
2709 return fulldelete($this->directory);
2710 }
2711}
2712
2713/**
2714 * Data preset importer for existing presets
2715 */
2716class data_preset_existing_importer extends data_preset_importer {
2717 protected $userid;
2718 public function __construct($course, $cm, $module, $fullname) {
2719 global $USER;
2720 list($userid, $shortname) = explode('/', $fullname, 2);
0b3b8c30 2721 $context = context_module::instance($cm->id);
cba87c36
SH
2722 if ($userid && ($userid != $USER->id) && !has_capability('mod/data:manageuserpresets', $context) && !has_capability('mod/data:viewalluserpresets', $context)) {
2723 throw new coding_exception('Invalid preset provided');
2724 }
2725
2726 $this->userid = $userid;
2727 $filepath = data_preset_path($course, $userid, $shortname);
2728 parent::__construct($course, $cm, $module, $filepath);
2729 }
2730 public function get_userid() {
2731 return $this->userid;
2732 }
2733}
2734
4636bf83 2735/**
2736 * @global object
2737 * @global object
2738 * @param object $course
2739 * @param int $userid
2740 * @param string $shortname
2741 * @return string
2742 */
50194545 2743function data_preset_path($course, $userid, $shortname) {
2744 global $USER, $CFG;
b572ce19 2745
0b3b8c30 2746 $context = context_course::instance($course->id);
b572ce19 2747
50194545 2748 $userid = (int)$userid;
b572ce19 2749
263819c8 2750 $path = null;
50194545 2751 if ($userid > 0 && ($userid == $USER->id || has_capability('mod/data:viewalluserpresets', $context))) {
263819c8 2752 $path = $CFG->dataroot.'/data/preset/'.$userid.'/'.$shortname;
50194545 2753 } else if ($userid == 0) {
263819c8 2754 $path = $CFG->dirroot.'/mod/data/preset/'.$shortname;
50194545 2755 } else if ($userid < 0) {
7aa06e6d 2756 $path = $CFG->tempdir.'/data/'.-$userid.'/'.$shortname;
50194545 2757 }
b572ce19 2758
263819c8 2759 return $path;
50194545 2760}
0b5a80a1 2761
2762/**
2763 * Implementation of the function for printing the form elements that control
2764 * whether the course reset functionality affects the data.
4636bf83 2765 *
0b5a80a1 2766 * @param $mform form passed by reference
2767 */
2768function data_reset_course_form_definition(&$mform) {
2769 $mform->addElement('header', 'dataheader', get_string('modulenameplural', 'data'));
2770 $mform->addElement('checkbox', 'reset_data', get_string('deleteallentries','data'));
2771
2772 $mform->addElement('checkbox', 'reset_data_notenrolled', get_string('deletenotenrolled', 'data'));
2773 $mform->disabledIf('reset_data_notenrolled', 'reset_data', 'checked');
2774
2775 $mform->addElement('checkbox', 'reset_data_ratings', get_string('deleteallratings'));
2776 $mform->disabledIf('reset_data_ratings', 'reset_data', 'checked');
2777
2778 $mform->addElement('checkbox', 'reset_data_comments', get_string('deleteallcomments'));
2779 $mform->disabledIf('reset_data_comments', 'reset_data', 'checked');
2780}
2781
2782/**
2783 * Course reset form defaults.
4636bf83 2784 * @return array
0b5a80a1 2785 */
2786function data_reset_course_form_defaults($course) {
2787 return array('reset_data'=>0, 'reset_data_ratings'=>1, 'reset_data_comments'=>1, 'reset_data_notenrolled'=>0);
2788}
2789
2790/**
2791 * Removes all grades from gradebook
4636bf83 2792 *
2793 * @global object
2794 * @global object
0b5a80a1 2795 * @param int $courseid
4636bf83 2796 * @param string $type optional type
0b5a80a1 2797 */
2798function data_reset_gradebook($courseid, $type='') {
9c00b5d7 2799 global $CFG, $DB;
0b5a80a1 2800
2801 $sql = "SELECT d.*, cm.idnumber as cmidnumber, d.course as courseid
9c00b5d7 2802 FROM {data} d, {course_modules} cm, {modules} m
2803 WHERE m.name='data' AND m.id=cm.module AND cm.instance=d.id AND d.course=?";
0b5a80a1 2804
9c00b5d7 2805 if ($datas = $DB->get_records_sql($sql, array($courseid))) {
0b5a80a1 2806 foreach ($datas as $data) {
2807 data_grade_item_update($data, 'reset');
2808 }
2809 }
2810}
2811
2812/**
72d2982e 2813 * Actual implementation of the reset course functionality, delete all the
0b5a80a1 2814 * data responses for course $data->courseid.
4636bf83 2815 *
2816 * @global object
2817 * @global object
2818 * @param object $data the data submitted from the reset course.
0b5a80a1 2819 * @return array status array
2820 */
2821function data_reset_userdata($data) {
9c00b5d7 2822 global $CFG, $DB;
0b5a80a1 2823 require_once($CFG->libdir.'/filelib.php');
d251b259 2824 require_once($CFG->dirroot.'/rating/lib.php');
b572ce19 2825
0b5a80a1 2826 $componentstr = get_string('modulenameplural', 'data');
2827 $status = array();
b572ce19 2828
0b5a80a1 2829 $allrecordssql = "SELECT r.id
9c00b5d7 2830 FROM {data_records} r
2831 INNER JOIN {data} d ON r.dataid = d.id
2832 WHERE d.course = ?";
0b5a80a1 2833
2834 $alldatassql = "SELECT d.id
9c00b5d7 2835 FROM {data} d
2836 WHERE d.course=?";
0b5a80a1 2837
d251b259 2838 $rm = new rating_manager();
2b04c41c
SH
2839 $ratingdeloptions = new stdClass;
2840 $ratingdeloptions->component = 'mod_data';
2841 $ratingdeloptions->ratingarea = 'entry';
d251b259 2842
4e457296
MN
2843 // Set the file storage - may need it to remove files later.
2844 $fs = get_file_storage();
2845
0b5a80a1 2846 // delete entries if requested
2847 if (!empty($data->reset_data)) {
e998effa 2848 $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
9c00b5d7 2849 $DB->delete_records_select('data_content', "recordid IN ($allrecordssql)", array($data->courseid));
2850 $DB->delete_records_select('data_records', "dataid IN ($alldatassql)", array($data->courseid));
0b5a80a1 2851
9c00b5d7 2852 if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
0b5a80a1 2853 foreach ($datas as $dataid=>$unused) {
d251b259
AD
2854 if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
2855 continue;
2856 }
0b3b8c30 2857 $datacontext = context_module::instance($cm->id);
d251b259 2858
4e457296
MN
2859 // Delete any files that may exist.
2860 $fs->delete_area_files($datacontext->id, 'mod_data', 'content');
2861
d251b259
AD
2862 $ratingdeloptions->contextid = $datacontext->id;
2863 $rm->delete_ratings($ratingdeloptions);
0b5a80a1 2864 }
2865 }
b572ce19 2866
0b5a80a1 2867 if (empty($data->reset_gradebook_grades)) {
2868 // remove all grades from gradebook
2869 data_reset_gradebook($data->courseid);
2870 }
2871 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallentries', 'data'), 'error'=>false);
2872 }
2873
2874 // remove entries by users not enrolled into course
2875 if (!empty($data->reset_data_notenrolled)) {
2876 $recordssql = "SELECT r.id, r.userid, r.dataid, u.id AS userexists, u.deleted AS userdeleted
9c00b5d7 2877 FROM {data_records} r
2878 JOIN {data} d ON r.dataid = d.id
2879 LEFT JOIN {user} u ON r.userid = u.id
2880 WHERE d.course = ? AND r.userid > 0";
0b5a80a1 2881
0b3b8c30 2882 $course_context = context_course::instance($data->courseid);
0b5a80a1 2883 $notenrolled = array();
2884 $fields = array();
6b1b1d03
EL
2885 $rs = $DB->get_recordset_sql($recordssql, array($data->courseid));
2886 foreach ($rs as $record) {
2887 if (array_key_exists($record->userid, $notenrolled) or !$record->userexists or $record->userdeleted
2888 or !is_enrolled($course_context, $record->userid)) {
2889 //delete ratings
2890 if (!$cm = get_coursemodule_from_instance('data', $record->dataid)) {
2891 continue;
2892 }
0b3b8c30 2893 $datacontext = context_module::instance($cm->id);
6b1b1d03
EL
2894 $ratingdeloptions->contextid = $datacontext->id;
2895 $ratingdeloptions->itemid = $record->id;
2896 $rm->delete_ratings($ratingdeloptions);
2897
4e457296
MN
2898 // Delete any files that may exist.
2899 if ($contents = $DB->get_records('data_content', array('recordid' => $record->id), '', 'id')) {
2900 foreach ($contents as $content) {
2901 $fs->delete_area_files($datacontext->id, 'mod_data', 'content', $content->id);
0b5a80a1 2902 }
0b5a80a1 2903 }
6b1b1d03 2904 $notenrolled[$record->userid] = true;
4e457296
MN
2905
2906 $DB->delete_records('comments', array('itemid' => $record->id, 'commentarea' => 'database_entry'));
2907 $DB->delete_records('data_content', array('recordid' => $record->id));
2908 $DB->delete_records('data_records', array('id' => $record->id));
0b5a80a1 2909 }
0b5a80a1 2910 }
6b1b1d03
EL
2911 $rs->close();
2912 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'data'), 'error'=>false);
0b5a80a1 2913 }
2914
2915 // remove all ratings
2916 if (!empty($data->reset_data_ratings)) {
d251b259
AD
2917 if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
2918 foreach ($datas as $dataid=>$unused) {
2919 if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
2920 continue;
2921 }
0b3b8c30 2922 $datacontext = context_module::instance($cm->id);
d251b259
AD
2923
2924 $ratingdeloptions->contextid = $datacontext->id;
2925 $rm->delete_ratings($ratingdeloptions);
2926 }
2927 }
0b5a80a1 2928
2929 if (empty($data->reset_gradebook_grades)) {
2930 // remove all grades from gradebook
2931 data_reset_gradebook($data->courseid);
2932 }
b572ce19 2933
0b5a80a1 2934 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false);
2935 }
2936
2937 // remove all comments
2938 if (!empty($data->reset_data_comments)) {
e998effa 2939 $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
0b5a80a1 2940 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false);
2941 }
2942
b8b554ac 2943 // updating dates - shift may be negative too
0b5a80a1 2944 if ($data->timeshift) {
0d14fcbc
AA
2945 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
2946 // See MDL-9367.
2947 shift_course_mod_dates('data', array('timeavailablefrom', 'timeavailableto',
2948 'timeviewfrom', 'timeviewto', 'assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
0b5a80a1 2949 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
2950 }
b572ce19 2951
0b5a80a1 2952 return $status;
2953}
f432bebf 2954
2955/**
2956 * Returns all other caps used in module
4636bf83 2957 *
2958 * @return array
f432bebf 2959 */
2960function data_get_extra_capabilities() {
16b86ae4 2961 return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate', 'moodle/comment:view', 'moodle/comment:post', 'moodle/comment:delete');
f432bebf 2962}
2963
18a2a0cb 2964/**
2965 * @param string $feature FEATURE_xx constant for requested feature
2966 * @return mixed True if module supports feature, null if doesn't know
2967 */
2968function data_supports($feature) {
2969 switch($feature) {
42f103be 2970 case FEATURE_GROUPS: return true;
2971 case FEATURE_GROUPINGS: return true;
dc5c2bd9 2972 case FEATURE_MOD_INTRO: return true;
18a2a0cb 2973 case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
cf873ada 2974 case FEATURE_COMPLETION_HAS_RULES: return true;
42f103be 2975 case FEATURE_GRADE_HAS_GRADE: return true;
2976 case FEATURE_GRADE_OUTCOMES: return true;
d251b259 2977 case FEATURE_RATE: return true;
fd3f6bf9 2978 case FEATURE_BACKUP_MOODLE2: return true;
3e4c2435 2979 case FEATURE_SHOW_DESCRIPTION: return true;
6492401d 2980 case FEATURE_COMMENT: return true;
42f103be 2981
18a2a0cb 2982 default: return null;
17da2e6f 2983 }
18a2a0cb 2984}
4636bf83 2985/**
2986 * @global object
2987 * @param array $export
2988 * @param string $delimiter_name
2989 * @param object $database
2990 * @param int $count
2991 * @param bool $return
2992 * @return string|void
2993 */
9a5abd1b 2994function data_export_csv($export, $delimiter_name, $database, $count, $return=false) {
ecd06483 2995 global $CFG;
2996 require_once($CFG->libdir . '/csvlib.class.php');
9a5abd1b
AG
2997
2998 $filename = $database . '-' . $count . '-record';
20988152 2999 if ($count > 1) {
3000 $filename .= 's';
3001 }
9a5abd1b
AG
3002 if ($return) {
3003 return csv_export_writer::print_array($export, $delimiter_name, '"', true);
3004 } else {
3005 csv_export_writer::download_array($filename, $export, $delimiter_name);
20988152 3006 }
3007}
3008
4636bf83 3009/**
3010 * @global object
3011 * @param array $export
3012 * @param string $dataname
3013 * @param int $count
3014 * @return string
3015 */
1bf8c6b2 3016function data_export_xls($export, $dataname, $count) {
20988152 3017 global $CFG;
3018 require_once("$CFG->libdir/excellib.class.php");
fa7f82be 3019 $filename = clean_filename("{$dataname}-{$count}_record");
20988152 3020 if ($count > 1) {
3021 $filename .= 's';
3022 }
3023 $filename .= clean_filename('-' . gmdate("Ymd_Hi"));
3024 $filename .= '.xls';
ecd06483 3025
3026 $filearg = '-';
ecd06483 3027 $workbook = new MoodleExcelWorkbook($filearg);
1bf8c6b2 3028 $workbook->send($filename);
20988152 3029 $worksheet = array();
75427a82 3030 $worksheet[0] = $workbook->add_worksheet('');
20988152 3031 $rowno = 0;
3032 foreach ($export as $row) {
3033 $colno = 0;
3034 foreach($row as $col) {
3035 $worksheet[0]->write($rowno, $colno, $col);
3036 $colno++;
3037 }
3038 $rowno++;
3039 }
3040 $workbook->close();
ecd06483 3041 return $filename;
20988152 3042}
3043
4636bf83 3044/**
3045 * @global object
3046 * @param array $export
3047 * @param string $dataname
3048 * @param int $count
3049 * @param string
3050 */
1bf8c6b2 3051function data_export_ods($export, $dataname, $count) {
20988152 3052 global $CFG;
3053 require_once("$CFG->libdir/odslib.class.php");
fa7f82be 3054 $filename = clean_filename("{$dataname}-{$count}_record");
20988152 3055 if ($count > 1) {
3056 $filename .= 's';
3057 }
3058 $filename .= clean_filename('-' . gmdate("Ymd_Hi"));
3059 $filename .= '.ods';
ecd06483 3060 $filearg = '-';
1bf8c6b2 3061 $workbook = new MoodleODSWorkbook($filearg);
3062 $workbook->send($filename);
20988152 3063 $worksheet = array();
75427a82 3064 $worksheet[0] = $workbook->add_worksheet('');
20988152 3065 $rowno = 0;
3066 foreach ($export as $row) {
3067 $colno = 0;
3068 foreach($row as $col) {
3069 $worksheet[0]->write($rowno, $colno, $col);
3070 $colno++;
3071 }
3072 $rowno++;
3073 }
3074 $workbook->close();
ecd06483 3075 return $filename;
3076}
3077
4636bf83 3078/**
3079 * @global object
3080 * @param int $dataid
3081 * @param array $fields
3082 * @param array $selectedfields
83468603
AG
3083 * @param int $currentgroup group ID of the current group. This is used for
3084 * exporting data while maintaining group divisions.
6e12c215
AB
3085 * @param object $context the context in which the operation is performed (for capability checks)
3086 * @param bool $userdetails whether to include the details of the record author
3087 * @param bool $time whether to include time created/modified
3088 * @param bool $approval whether to include approval status
4636bf83 3089 * @return array
3090 */
4f304eeb
HB
3091function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null,
3092 $userdetails=false, $time=false, $approval=false) {
ecd06483 3093 global $DB;
3094
4f304eeb
HB
3095 if (is_null($context)) {
3096 $context = context_system::instance();
3097 }
3098 // exporting user data needs special permission
3099 $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context);
3100
ecd06483 3101 $exportdata = array();
3102
3103 // populate the header in first row of export
3104 foreach($fields as $key => $field) {
3105 if (!in_array($field->field->id, $selectedfields)) {
3106 // ignore values we aren't exporting
3107 unset($fields[$key]);
3108 } else {
3109 $exportdata[0][] = $field->field->name;
3110 }
3111 }
4f304eeb
HB
3112 if ($userdetails) {
3113 $exportdata[0][] = get_string('user');
3114 $exportdata[0][] = get_string('username');
3115 $exportdata[0][] = get_string('email');
3116 }
3117 if ($time) {
3118 $exportdata[0][] = get_string('timeadded', 'data');
3119 $exportdata[0][] = get_string('timemodified', 'data');
3120 }
3121 if ($approval) {
3122 $exportdata[0][] = get_string('approved', 'data');
3123 }
ecd06483 3124
3125 $datarecords = $DB->get_records('data_records', array('dataid'=>$dataid));
3126 ksort($datarecords);
3127 $line = 1;
3128 foreach($datarecords as $record) {
3129 // get content indexed by fieldid
83468603
AG
3130 if ($currentgroup) {
3131 $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?';
3132 $where = array($record->id, $currentgroup);
3133 } else {
3134 $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?';
3135 $where = array($record->id);
3136 }
3137
3138 if( $content = $DB->get_records_sql($select, $where) ) {
ecd06483 3139 foreach($fields as $field) {
3140 $contents = '';
3141 if(isset($content[$field->field->id])) {
3142 $contents = $field->export_text_value($content[$field->field->id]);
3143 }
3144 $exportdata[$line][] = $contents;
3145 }
4f304eeb
HB
3146 if ($userdetails) { // Add user details to the export data
3147 $userdata = get_complete_user_data('id', $record->userid);
3148 $exportdata[$line][] = fullname($userdata);
3149 $exportdata[$line][] = $userdata->username;
3150 $exportdata[$line][] = $userdata->email;
3151 }
3152 if ($time) { // Add time added / modified
3153 $exportdata[$line][] = userdate($record->timecreated);
3154 $exportdata[$line][] = userdate($record->timemodified);
3155 }
3156 if ($approval) { // Add approval status
3157 $exportdata[$line][] = (int) $record->approved;
3158 }
ecd06483 3159 }
3160 $line++;
3161 }
3162 $line--;
3163 return $exportdata;
3164}
3165
386b67d1
DM
3166////////////////////////////////////////////////////////////////////////////////
3167// File API //
3168////////////////////////////////////////////////////////////////////////////////
3169
8429163d 3170/**
3171 * Lists all browsable file areas
4636bf83 3172 *
d2b7803e
DC
3173 * @package mod_data
3174 * @category files
3175 * @param stdClass $course course object
3176 * @param stdClass $cm course module object
3177 * @param stdClass $context context object
4636bf83 3178 * @return array
8429163d 3179 */
3180function data_get_file_areas($course, $cm, $context) {
386b67d1 3181 return array('content' => get_string('areacontent', 'mod_data'));
8429163d 3182}
3183
00ecac72
MG
3184/**
3185 * File browsing support for data module.
3186 *
3187 * @param file_browser $browser
3188 * @param array $areas
3189 * @param stdClass $course
3190 * @param cm_info $cm
3191 * @param context $context
3192 * @param string $filearea
3193 * @param int $itemid
3194 * @param string $filepath
3195 * @param string $filename
3196 * @return file_info_stored file_info_stored instance or null if not found
3197 */
6b8b0b2e 3198function data_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
35ca63c1 3199 global $CFG, $DB, $USER;
00ecac72
MG
3200
3201 if ($context->contextlevel != CONTEXT_MODULE) {
3202 return null;
3203 }
3204
386b67d1
DM
3205 if (!isset($areas[$filearea])) {
3206 return null;
3207 }
00ecac72 3208
386b67d1
DM
3209 if (is_null($itemid)) {
3210 require_once($CFG->dirroot.'/mod/data/locallib.php');
3211 return new data_file_info_container($browser, $course, $cm, $context, $areas, $filearea);
3212 }
00ecac72 3213
386b67d1
DM
3214 if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) {
3215 return null;
3216 }
00ecac72 3217
386b67d1
DM
3218 if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) {
3219 return null;
3220 }
00ecac72 3221
386b67d1
DM
3222 if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) {
3223 return null;
3224 }
3225
3226 if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) {
3227 return null;
3228 }
3229
3230 //check if approved
3231 if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
3232 return null;
3233 }
3234