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