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