[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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/>. 17 18 /** 19 * @package mod_data 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 24 defined('MOODLE_INTERNAL') || die(); 25 26 // Some constants 27 define ('DATA_MAX_ENTRIES', 50); 28 define ('DATA_PERPAGE_SINGLE', 1); 29 30 define ('DATA_FIRSTNAME', -1); 31 define ('DATA_LASTNAME', -2); 32 define ('DATA_APPROVED', -3); 33 define ('DATA_TIMEADDED', 0); 34 define ('DATA_TIMEMODIFIED', -4); 35 36 define ('DATA_CAP_EXPORT', 'mod/data:viewalluserpresets'); 37 38 define('DATA_PRESET_COMPONENT', 'mod_data'); 39 define('DATA_PRESET_FILEAREA', 'site_presets'); 40 define('DATA_PRESET_CONTEXT', SYSCONTEXTID); 41 42 // Users having assigned the default role "Non-editing teacher" can export database records 43 // Using the mod/data capability "viewalluserpresets" existing in Moodle 1.9.x. 44 // In Moodle >= 2, new roles may be introduced and used instead. 45 46 /** 47 * @package mod_data 48 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 */ 51 class data_field_base { // Base class for Database Field Types (see field/*/field.class.php) 52 53 /** @var string Subclasses must override the type with their name */ 54 var $type = 'unknown'; 55 /** @var object The database object that this field belongs to */ 56 var $data = NULL; 57 /** @var object The field object itself, if we know it */ 58 var $field = NULL; 59 /** @var int Width of the icon for this fieldtype */ 60 var $iconwidth = 16; 61 /** @var int Width of the icon for this fieldtype */ 62 var $iconheight = 16; 63 /** @var object course module or cmifno */ 64 var $cm; 65 /** @var object activity context */ 66 var $context; 67 68 /** 69 * Constructor function 70 * 71 * @global object 72 * @uses CONTEXT_MODULE 73 * @param int $field 74 * @param int $data 75 * @param int $cm 76 */ 77 function __construct($field=0, $data=0, $cm=0) { // Field or data or both, each can be id or object 78 global $DB; 79 80 if (empty($field) && empty($data)) { 81 print_error('missingfield', 'data'); 82 } 83 84 if (!empty($field)) { 85 if (is_object($field)) { 86 $this->field = $field; // Programmer knows what they are doing, we hope 87 } else if (!$this->field = $DB->get_record('data_fields', array('id'=>$field))) { 88 print_error('invalidfieldid', 'data'); 89 } 90 if (empty($data)) { 91 if (!$this->data = $DB->get_record('data', array('id'=>$this->field->dataid))) { 92 print_error('invalidid', 'data'); 93 } 94 } 95 } 96 97 if (empty($this->data)) { // We need to define this properly 98 if (!empty($data)) { 99 if (is_object($data)) { 100 $this->data = $data; // Programmer knows what they are doing, we hope 101 } else if (!$this->data = $DB->get_record('data', array('id'=>$data))) { 102 print_error('invalidid', 'data'); 103 } 104 } else { // No way to define it! 105 print_error('missingdata', 'data'); 106 } 107 } 108 109 if ($cm) { 110 $this->cm = $cm; 111 } else { 112 $this->cm = get_coursemodule_from_instance('data', $this->data->id); 113 } 114 115 if (empty($this->field)) { // We need to define some default values 116 $this->define_default_field(); 117 } 118 119 $this->context = context_module::instance($this->cm->id); 120 } 121 122 123 /** 124 * This field just sets up a default field object 125 * 126 * @return bool 127 */ 128 function define_default_field() { 129 global $OUTPUT; 130 if (empty($this->data->id)) { 131 echo $OUTPUT->notification('Programmer error: dataid not defined in field class'); 132 } 133 $this->field = new stdClass(); 134 $this->field->id = 0; 135 $this->field->dataid = $this->data->id; 136 $this->field->type = $this->type; 137 $this->field->param1 = ''; 138 $this->field->param2 = ''; 139 $this->field->param3 = ''; 140 $this->field->name = ''; 141 $this->field->description = ''; 142 $this->field->required = false; 143 144 return true; 145 } 146 147 /** 148 * Set up the field object according to data in an object. Now is the time to clean it! 149 * 150 * @return bool 151 */ 152 function define_field($data) { 153 $this->field->type = $this->type; 154 $this->field->dataid = $this->data->id; 155 156 $this->field->name = trim($data->name); 157 $this->field->description = trim($data->description); 158 $this->field->required = !empty($data->required) ? 1 : 0; 159 160 if (isset($data->param1)) { 161 $this->field->param1 = trim($data->param1); 162 } 163 if (isset($data->param2)) { 164 $this->field->param2 = trim($data->param2); 165 } 166 if (isset($data->param3)) { 167 $this->field->param3 = trim($data->param3); 168 } 169 if (isset($data->param4)) { 170 $this->field->param4 = trim($data->param4); 171 } 172 if (isset($data->param5)) { 173 $this->field->param5 = trim($data->param5); 174 } 175 176 return true; 177 } 178 179 /** 180 * Insert a new field in the database 181 * We assume the field object is already defined as $this->field 182 * 183 * @global object 184 * @return bool 185 */ 186 function insert_field() { 187 global $DB, $OUTPUT; 188 189 if (empty($this->field)) { 190 echo $OUTPUT->notification('Programmer error: Field has not been defined yet! See define_field()'); 191 return false; 192 } 193 194 $this->field->id = $DB->insert_record('data_fields',$this->field); 195 196 // Trigger an event for creating this field. 197 $event = \mod_data\event\field_created::create(array( 198 'objectid' => $this->field->id, 199 'context' => $this->context, 200 'other' => array( 201 'fieldname' => $this->field->name, 202 'dataid' => $this->data->id 203 ) 204 )); 205 $event->trigger(); 206 207 return true; 208 } 209 210 211 /** 212 * Update a field in the database 213 * 214 * @global object 215 * @return bool 216 */ 217 function update_field() { 218 global $DB; 219 220 $DB->update_record('data_fields', $this->field); 221 222 // Trigger an event for updating this field. 223 $event = \mod_data\event\field_updated::create(array( 224 'objectid' => $this->field->id, 225 'context' => $this->context, 226 'other' => array( 227 'fieldname' => $this->field->name, 228 'dataid' => $this->data->id 229 ) 230 )); 231 $event->trigger(); 232 233 return true; 234 } 235 236 /** 237 * Delete a field completely 238 * 239 * @global object 240 * @return bool 241 */ 242 function delete_field() { 243 global $DB; 244 245 if (!empty($this->field->id)) { 246 // Get the field before we delete it. 247 $field = $DB->get_record('data_fields', array('id' => $this->field->id)); 248 249 $this->delete_content(); 250 $DB->delete_records('data_fields', array('id'=>$this->field->id)); 251 252 // Trigger an event for deleting this field. 253 $event = \mod_data\event\field_deleted::create(array( 254 'objectid' => $this->field->id, 255 'context' => $this->context, 256 'other' => array( 257 'fieldname' => $this->field->name, 258 'dataid' => $this->data->id 259 ) 260 )); 261 $event->add_record_snapshot('data_fields', $field); 262 $event->trigger(); 263 } 264 265 return true; 266 } 267 268 /** 269 * Print the relevant form element in the ADD template for this field 270 * 271 * @global object 272 * @param int $recordid 273 * @return string 274 */ 275 function display_add_field($recordid=0, $formdata=null) { 276 global $DB, $OUTPUT; 277 278 if ($formdata) { 279 $fieldname = 'field_' . $this->field->id; 280 $content = $formdata->$fieldname; 281 } else if ($recordid) { 282 $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid)); 283 } else { 284 $content = ''; 285 } 286 287 // beware get_field returns false for new, empty records MDL-18567 288 if ($content===false) { 289 $content=''; 290 } 291 292 $str = '<div title="' . s($this->field->description) . '">'; 293 $str .= '<label for="field_'.$this->field->id.'"><span class="accesshide">'.$this->field->name.'</span>'; 294 if ($this->field->required) { 295 $image = html_writer::img($OUTPUT->pix_url('req'), get_string('requiredelement', 'form'), 296 array('class' => 'req', 'title' => get_string('requiredelement', 'form'))); 297 $str .= html_writer::div($image, 'inline-req'); 298 } 299 $str .= '</label><input class="basefieldinput mod-data-input" type="text" name="field_'.$this->field->id.'"'; 300 $str .= ' id="field_' . $this->field->id . '" value="'.s($content).'" />'; 301 $str .= '</div>'; 302 303 return $str; 304 } 305 306 /** 307 * Print the relevant form element to define the attributes for this field 308 * viewable by teachers only. 309 * 310 * @global object 311 * @global object 312 * @return void Output is echo'd 313 */ 314 function display_edit_field() { 315 global $CFG, $DB, $OUTPUT; 316 317 if (empty($this->field)) { // No field has been defined yet, try and make one 318 $this->define_default_field(); 319 } 320 echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide'); 321 322 echo '<form id="editfield" action="'.$CFG->wwwroot.'/mod/data/field.php" method="post">'."\n"; 323 echo '<input type="hidden" name="d" value="'.$this->data->id.'" />'."\n"; 324 if (empty($this->field->id)) { 325 echo '<input type="hidden" name="mode" value="add" />'."\n"; 326 $savebutton = get_string('add'); 327 } else { 328 echo '<input type="hidden" name="fid" value="'.$this->field->id.'" />'."\n"; 329 echo '<input type="hidden" name="mode" value="update" />'."\n"; 330 $savebutton = get_string('savechanges'); 331 } 332 echo '<input type="hidden" name="type" value="'.$this->type.'" />'."\n"; 333 echo '<input name="sesskey" value="'.sesskey().'" type="hidden" />'."\n"; 334 335 echo $OUTPUT->heading($this->name(), 3); 336 337 require_once($CFG->dirroot.'/mod/data/field/'.$this->type.'/mod.html'); 338 339 echo '<div class="mdl-align">'; 340 echo '<input type="submit" value="'.$savebutton.'" />'."\n"; 341 echo '<input type="submit" name="cancel" value="'.get_string('cancel').'" />'."\n"; 342 echo '</div>'; 343 344 echo '</form>'; 345 346 echo $OUTPUT->box_end(); 347 } 348 349 /** 350 * Display the content of the field in browse mode 351 * 352 * @global object 353 * @param int $recordid 354 * @param object $template 355 * @return bool|string 356 */ 357 function display_browse_field($recordid, $template) { 358 global $DB; 359 360 if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { 361 if (isset($content->content)) { 362 $options = new stdClass(); 363 if ($this->field->param1 == '1') { // We are autolinking this field, so disable linking within us 364 //$content->content = '<span class="nolink">'.$content->content.'</span>'; 365 //$content->content1 = FORMAT_HTML; 366 $options->filter=false; 367 } 368 $options->para = false; 369 $str = format_text($content->content, $content->content1, $options); 370 } else { 371 $str = ''; 372 } 373 return $str; 374 } 375 return false; 376 } 377 378 /** 379 * Update the content of one data field in the data_content table 380 * @global object 381 * @param int $recordid 382 * @param mixed $value 383 * @param string $name 384 * @return bool 385 */ 386 function update_content($recordid, $value, $name=''){ 387 global $DB; 388 389 $content = new stdClass(); 390 $content->fieldid = $this->field->id; 391 $content->recordid = $recordid; 392 $content->content = clean_param($value, PARAM_NOTAGS); 393 394 if ($oldcontent = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) { 395 $content->id = $oldcontent->id; 396 return $DB->update_record('data_content', $content); 397 } else { 398 return $DB->insert_record('data_content', $content); 399 } 400 } 401 402 /** 403 * Delete all content associated with the field 404 * 405 * @global object 406 * @param int $recordid 407 * @return bool 408 */ 409 function delete_content($recordid=0) { 410 global $DB; 411 412 if ($recordid) { 413 $conditions = array('fieldid'=>$this->field->id, 'recordid'=>$recordid); 414 } else { 415 $conditions = array('fieldid'=>$this->field->id); 416 } 417 418 $rs = $DB->get_recordset('data_content', $conditions); 419 if ($rs->valid()) { 420 $fs = get_file_storage(); 421 foreach ($rs as $content) { 422 $fs->delete_area_files($this->context->id, 'mod_data', 'content', $content->id); 423 } 424 } 425 $rs->close(); 426 427 return $DB->delete_records('data_content', $conditions); 428 } 429 430 /** 431 * Check if a field from an add form is empty 432 * 433 * @param mixed $value 434 * @param mixed $name 435 * @return bool 436 */ 437 function notemptyfield($value, $name) { 438 return !empty($value); 439 } 440 441 /** 442 * Just in case a field needs to print something before the whole form 443 */ 444 function print_before_form() { 445 } 446 447 /** 448 * Just in case a field needs to print something after the whole form 449 */ 450 function print_after_form() { 451 } 452 453 454 /** 455 * Returns the sortable field for the content. By default, it's just content 456 * but for some plugins, it could be content 1 - content4 457 * 458 * @return string 459 */ 460 function get_sort_field() { 461 return 'content'; 462 } 463 464 /** 465 * Returns the SQL needed to refer to the column. Some fields may need to CAST() etc. 466 * 467 * @param string $fieldname 468 * @return string $fieldname 469 */ 470 function get_sort_sql($fieldname) { 471 return $fieldname; 472 } 473 474 /** 475 * Returns the name/type of the field 476 * 477 * @return string 478 */ 479 function name() { 480 return get_string('name'.$this->type, 'data'); 481 } 482 483 /** 484 * Prints the respective type icon 485 * 486 * @global object 487 * @return string 488 */ 489 function image() { 490 global $OUTPUT; 491 492 $params = array('d'=>$this->data->id, 'fid'=>$this->field->id, 'mode'=>'display', 'sesskey'=>sesskey()); 493 $link = new moodle_url('/mod/data/field.php', $params); 494 $str = '<a href="'.$link->out().'">'; 495 $str .= '<img src="'.$OUTPUT->pix_url('field/'.$this->type, 'data') . '" '; 496 $str .= 'height="'.$this->iconheight.'" width="'.$this->iconwidth.'" alt="'.$this->type.'" title="'.$this->type.'" /></a>'; 497 return $str; 498 } 499 500 /** 501 * Per default, it is assumed that fields support text exporting. 502 * Override this (return false) on fields not supporting text exporting. 503 * 504 * @return bool true 505 */ 506 function text_export_supported() { 507 return true; 508 } 509 510 /** 511 * Per default, return the record's text value only from the "content" field. 512 * Override this in fields class if necesarry. 513 * 514 * @param string $record 515 * @return string 516 */ 517 function export_text_value($record) { 518 if ($this->text_export_supported()) { 519 return $record->content; 520 } 521 } 522 523 /** 524 * @param string $relativepath 525 * @return bool false 526 */ 527 function file_ok($relativepath) { 528 return false; 529 } 530 } 531 532 533 /** 534 * Given a template and a dataid, generate a default case template 535 * 536 * @global object 537 * @param object $data 538 * @param string template [addtemplate, singletemplate, listtempalte, rsstemplate] 539 * @param int $recordid 540 * @param bool $form 541 * @param bool $update 542 * @return bool|string 543 */ 544 function data_generate_default_template(&$data, $template, $recordid=0, $form=false, $update=true) { 545 global $DB; 546 547 if (!$data && !$template) { 548 return false; 549 } 550 if ($template == 'csstemplate' or $template == 'jstemplate' ) { 551 return ''; 552 } 553 554 // get all the fields for that database 555 if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'id')) { 556 557 $table = new html_table(); 558 $table->attributes['class'] = 'mod-data-default-template ##approvalstatus##'; 559 $table->colclasses = array('template-field', 'template-token'); 560 $table->data = array(); 561 foreach ($fields as $field) { 562 if ($form) { // Print forms instead of data 563 $fieldobj = data_get_field($field, $data); 564 $token = $fieldobj->display_add_field($recordid, null); 565 } else { // Just print the tag 566 $token = '[['.$field->name.']]'; 567 } 568 $table->data[] = array( 569 $field->name.': ', 570 $token 571 ); 572 } 573 if ($template == 'listtemplate') { 574 $cell = new html_table_cell('##edit## ##more## ##delete## ##approve## ##disapprove## ##export##'); 575 $cell->colspan = 2; 576 $cell->attributes['class'] = 'controls'; 577 $table->data[] = new html_table_row(array($cell)); 578 } else if ($template == 'singletemplate') { 579 $cell = new html_table_cell('##edit## ##delete## ##approve## ##disapprove## ##export##'); 580 $cell->colspan = 2; 581 $cell->attributes['class'] = 'controls'; 582 $table->data[] = new html_table_row(array($cell)); 583 } else if ($template == 'asearchtemplate') { 584 $row = new html_table_row(array(get_string('authorfirstname', 'data').': ', '##firstname##')); 585 $row->attributes['class'] = 'searchcontrols'; 586 $table->data[] = $row; 587 $row = new html_table_row(array(get_string('authorlastname', 'data').': ', '##lastname##')); 588 $row->attributes['class'] = 'searchcontrols'; 589 $table->data[] = $row; 590 } 591 592 $str = ''; 593 if ($template == 'listtemplate'){ 594 $str .= '##delcheck##'; 595 $str .= html_writer::empty_tag('br'); 596 } 597 598 $str .= html_writer::start_tag('div', array('class' => 'defaulttemplate')); 599 $str .= html_writer::table($table); 600 $str .= html_writer::end_tag('div'); 601 if ($template == 'listtemplate'){ 602 $str .= html_writer::empty_tag('hr'); 603 } 604 605 if ($update) { 606 $newdata = new stdClass(); 607 $newdata->id = $data->id; 608 $newdata->{$template} = $str; 609 $DB->update_record('data', $newdata); 610 $data->{$template} = $str; 611 } 612 613 return $str; 614 } 615 } 616 617 618 /** 619 * Search for a field name and replaces it with another one in all the 620 * form templates. Set $newfieldname as '' if you want to delete the 621 * field from the form. 622 * 623 * @global object 624 * @param object $data 625 * @param string $searchfieldname 626 * @param string $newfieldname 627 * @return bool 628 */ 629 function data_replace_field_in_templates($data, $searchfieldname, $newfieldname) { 630 global $DB; 631 632 if (!empty($newfieldname)) { 633 $prestring = '[['; 634 $poststring = ']]'; 635 $idpart = '#id'; 636 637 } else { 638 $prestring = ''; 639 $poststring = ''; 640 $idpart = ''; 641 } 642 643 $newdata = new stdClass(); 644 $newdata->id = $data->id; 645 $newdata->singletemplate = str_ireplace('[['.$searchfieldname.']]', 646 $prestring.$newfieldname.$poststring, $data->singletemplate); 647 648 $newdata->listtemplate = str_ireplace('[['.$searchfieldname.']]', 649 $prestring.$newfieldname.$poststring, $data->listtemplate); 650 651 $newdata->addtemplate = str_ireplace('[['.$searchfieldname.']]', 652 $prestring.$newfieldname.$poststring, $data->addtemplate); 653 654 $newdata->addtemplate = str_ireplace('[['.$searchfieldname.'#id]]', 655 $prestring.$newfieldname.$idpart.$poststring, $data->addtemplate); 656 657 $newdata->rsstemplate = str_ireplace('[['.$searchfieldname.']]', 658 $prestring.$newfieldname.$poststring, $data->rsstemplate); 659 660 return $DB->update_record('data', $newdata); 661 } 662 663 664 /** 665 * Appends a new field at the end of the form template. 666 * 667 * @global object 668 * @param object $data 669 * @param string $newfieldname 670 */ 671 function data_append_new_field_to_templates($data, $newfieldname) { 672 global $DB; 673 674 $newdata = new stdClass(); 675 $newdata->id = $data->id; 676 $change = false; 677 678 if (!empty($data->singletemplate)) { 679 $newdata->singletemplate = $data->singletemplate.' [[' . $newfieldname .']]'; 680 $change = true; 681 } 682 if (!empty($data->addtemplate)) { 683 $newdata->addtemplate = $data->addtemplate.' [[' . $newfieldname . ']]'; 684 $change = true; 685 } 686 if (!empty($data->rsstemplate)) { 687 $newdata->rsstemplate = $data->singletemplate.' [[' . $newfieldname . ']]'; 688 $change = true; 689 } 690 if ($change) { 691 $DB->update_record('data', $newdata); 692 } 693 } 694 695 696 /** 697 * given a field name 698 * this function creates an instance of the particular subfield class 699 * 700 * @global object 701 * @param string $name 702 * @param object $data 703 * @return object|bool 704 */ 705 function data_get_field_from_name($name, $data){ 706 global $DB; 707 708 $field = $DB->get_record('data_fields', array('name'=>$name, 'dataid'=>$data->id)); 709 710 if ($field) { 711 return data_get_field($field, $data); 712 } else { 713 return false; 714 } 715 } 716 717 /** 718 * given a field id 719 * this function creates an instance of the particular subfield class 720 * 721 * @global object 722 * @param int $fieldid 723 * @param object $data 724 * @return bool|object 725 */ 726 function data_get_field_from_id($fieldid, $data){ 727 global $DB; 728 729 $field = $DB->get_record('data_fields', array('id'=>$fieldid, 'dataid'=>$data->id)); 730 731 if ($field) { 732 return data_get_field($field, $data); 733 } else { 734 return false; 735 } 736 } 737 738 /** 739 * given a field id 740 * this function creates an instance of the particular subfield class 741 * 742 * @global object 743 * @param string $type 744 * @param object $data 745 * @return object 746 */ 747 function data_get_field_new($type, $data) { 748 global $CFG; 749 750 require_once($CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php'); 751 $newfield = 'data_field_'.$type; 752 $newfield = new $newfield(0, $data); 753 return $newfield; 754 } 755 756 /** 757 * returns a subclass field object given a record of the field, used to 758 * invoke plugin methods 759 * input: $param $field - record from db 760 * 761 * @global object 762 * @param object $field 763 * @param object $data 764 * @param object $cm 765 * @return object 766 */ 767 function data_get_field($field, $data, $cm=null) { 768 global $CFG; 769 770 if ($field) { 771 require_once('field/'.$field->type.'/field.class.php'); 772 $newfield = 'data_field_'.$field->type; 773 $newfield = new $newfield($field, $data, $cm); 774 return $newfield; 775 } 776 } 777 778 779 /** 780 * Given record object (or id), returns true if the record belongs to the current user 781 * 782 * @global object 783 * @global object 784 * @param mixed $record record object or id 785 * @return bool 786 */ 787 function data_isowner($record) { 788 global $USER, $DB; 789 790 if (!isloggedin()) { // perf shortcut 791 return false; 792 } 793 794 if (!is_object($record)) { 795 if (!$record = $DB->get_record('data_records', array('id'=>$record))) { 796 return false; 797 } 798 } 799 800 return ($record->userid == $USER->id); 801 } 802 803 /** 804 * has a user reached the max number of entries? 805 * 806 * @param object $data 807 * @return bool 808 */ 809 function data_atmaxentries($data){ 810 if (!$data->maxentries){ 811 return false; 812 813 } else { 814 return (data_numentries($data) >= $data->maxentries); 815 } 816 } 817 818 /** 819 * returns the number of entries already made by this user 820 * 821 * @global object 822 * @global object 823 * @param object $data 824 * @return int 825 */ 826 function data_numentries($data){ 827 global $USER, $DB; 828 $sql = 'SELECT COUNT(*) FROM {data_records} WHERE dataid=? AND userid=?'; 829 return $DB->count_records_sql($sql, array($data->id, $USER->id)); 830 } 831 832 /** 833 * function that takes in a dataid and adds a record 834 * this is used everytime an add template is submitted 835 * 836 * @global object 837 * @global object 838 * @param object $data 839 * @param int $groupid 840 * @return bool 841 */ 842 function data_add_record($data, $groupid=0){ 843 global $USER, $DB; 844 845 $cm = get_coursemodule_from_instance('data', $data->id); 846 $context = context_module::instance($cm->id); 847 848 $record = new stdClass(); 849 $record->userid = $USER->id; 850 $record->dataid = $data->id; 851 $record->groupid = $groupid; 852 $record->timecreated = $record->timemodified = time(); 853 if (has_capability('mod/data:approve', $context)) { 854 $record->approved = 1; 855 } else { 856 $record->approved = 0; 857 } 858 $record->id = $DB->insert_record('data_records', $record); 859 860 // Trigger an event for creating this record. 861 $event = \mod_data\event\record_created::create(array( 862 'objectid' => $record->id, 863 'context' => $context, 864 'other' => array( 865 'dataid' => $data->id 866 ) 867 )); 868 $event->trigger(); 869 870 return $record->id; 871 } 872 873 /** 874 * check the multple existence any tag in a template 875 * 876 * check to see if there are 2 or more of the same tag being used. 877 * 878 * @global object 879 * @param int $dataid, 880 * @param string $template 881 * @return bool 882 */ 883 function data_tags_check($dataid, $template) { 884 global $DB, $OUTPUT; 885 886 // first get all the possible tags 887 $fields = $DB->get_records('data_fields', array('dataid'=>$dataid)); 888 // then we generate strings to replace 889 $tagsok = true; // let's be optimistic 890 foreach ($fields as $field){ 891 $pattern="/\[\[" . preg_quote($field->name, '/') . "\]\]/i"; 892 if (preg_match_all($pattern, $template, $dummy)>1){ 893 $tagsok = false; 894 echo $OUTPUT->notification('[['.$field->name.']] - '.get_string('multipletags','data')); 895 } 896 } 897 // else return true 898 return $tagsok; 899 } 900 901 /** 902 * Adds an instance of a data 903 * 904 * @param stdClass $data 905 * @param mod_data_mod_form $mform 906 * @return int intance id 907 */ 908 function data_add_instance($data, $mform = null) { 909 global $DB, $CFG; 910 require_once($CFG->dirroot.'/mod/data/locallib.php'); 911 912 if (empty($data->assessed)) { 913 $data->assessed = 0; 914 } 915 916 if (empty($data->ratingtime) || empty($data->assessed)) { 917 $data->assesstimestart = 0; 918 $data->assesstimefinish = 0; 919 } 920 921 $data->timemodified = time(); 922 923 $data->id = $DB->insert_record('data', $data); 924 925 // Add calendar events if necessary. 926 data_set_events($data); 927 928 data_grade_item_update($data); 929 930 return $data->id; 931 } 932 933 /** 934 * updates an instance of a data 935 * 936 * @global object 937 * @param object $data 938 * @return bool 939 */ 940 function data_update_instance($data) { 941 global $DB, $CFG; 942 require_once($CFG->dirroot.'/mod/data/locallib.php'); 943 944 $data->timemodified = time(); 945 $data->id = $data->instance; 946 947 if (empty($data->assessed)) { 948 $data->assessed = 0; 949 } 950 951 if (empty($data->ratingtime) or empty($data->assessed)) { 952 $data->assesstimestart = 0; 953 $data->assesstimefinish = 0; 954 } 955 956 if (empty($data->notification)) { 957 $data->notification = 0; 958 } 959 960 $DB->update_record('data', $data); 961 962 // Add calendar events if necessary. 963 data_set_events($data); 964 965 data_grade_item_update($data); 966 967 return true; 968 969 } 970 971 /** 972 * deletes an instance of a data 973 * 974 * @global object 975 * @param int $id 976 * @return bool 977 */ 978 function data_delete_instance($id) { // takes the dataid 979 global $DB, $CFG; 980 981 if (!$data = $DB->get_record('data', array('id'=>$id))) { 982 return false; 983 } 984 985 $cm = get_coursemodule_from_instance('data', $data->id); 986 $context = context_module::instance($cm->id); 987 988 /// Delete all the associated information 989 990 // files 991 $fs = get_file_storage(); 992 $fs->delete_area_files($context->id, 'mod_data'); 993 994 // get all the records in this data 995 $sql = "SELECT r.id 996 FROM {data_records} r 997 WHERE r.dataid = ?"; 998 999 $DB->delete_records_select('data_content', "recordid IN ($sql)", array($id)); 1000 1001 // delete all the records and fields 1002 $DB->delete_records('data_records', array('dataid'=>$id)); 1003 $DB->delete_records('data_fields', array('dataid'=>$id)); 1004 1005 // Remove old calendar events. 1006 $events = $DB->get_records('event', array('modulename' => 'data', 'instance' => $id)); 1007 foreach ($events as $event) { 1008 $event = calendar_event::load($event); 1009 $event->delete(); 1010 } 1011 1012 // Delete the instance itself 1013 $result = $DB->delete_records('data', array('id'=>$id)); 1014 1015 // cleanup gradebook 1016 data_grade_item_delete($data); 1017 1018 return $result; 1019 } 1020 1021 /** 1022 * returns a summary of data activity of this user 1023 * 1024 * @global object 1025 * @param object $course 1026 * @param object $user 1027 * @param object $mod 1028 * @param object $data 1029 * @return object|null 1030 */ 1031 function data_user_outline($course, $user, $mod, $data) { 1032 global $DB, $CFG; 1033 require_once("$CFG->libdir/gradelib.php"); 1034 1035 $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id); 1036 if (empty($grades->items[0]->grades)) { 1037 $grade = false; 1038 } else { 1039 $grade = reset($grades->items[0]->grades); 1040 } 1041 1042 1043 if ($countrecords = $DB->count_records('data_records', array('dataid'=>$data->id, 'userid'=>$user->id))) { 1044 $result = new stdClass(); 1045 $result->info = get_string('numrecords', 'data', $countrecords); 1046 $lastrecord = $DB->get_record_sql('SELECT id,timemodified FROM {data_records} 1047 WHERE dataid = ? AND userid = ? 1048 ORDER BY timemodified DESC', array($data->id, $user->id), true); 1049 $result->time = $lastrecord->timemodified; 1050 if ($grade) { 1051 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade; 1052 } 1053 return $result; 1054 } else if ($grade) { 1055 $result = new stdClass(); 1056 $result->info = get_string('grade') . ': ' . $grade->str_long_grade; 1057 1058 //datesubmitted == time created. dategraded == time modified or time overridden 1059 //if grade was last modified by the user themselves use date graded. Otherwise use date submitted 1060 //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704 1061 if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) { 1062 $result->time = $grade->dategraded; 1063 } else { 1064 $result->time = $grade->datesubmitted; 1065 } 1066 1067 return $result; 1068 } 1069 return NULL; 1070 } 1071 1072 /** 1073 * Prints all the records uploaded by this user 1074 * 1075 * @global object 1076 * @param object $course 1077 * @param object $user 1078 * @param object $mod 1079 * @param object $data 1080 */ 1081 function data_user_complete($course, $user, $mod, $data) { 1082 global $DB, $CFG, $OUTPUT; 1083 require_once("$CFG->libdir/gradelib.php"); 1084 1085 $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id); 1086 if (!empty($grades->items[0]->grades)) { 1087 $grade = reset($grades->items[0]->grades); 1088 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade); 1089 if ($grade->str_feedback) { 1090 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback); 1091 } 1092 } 1093 1094 if ($records = $DB->get_records('data_records', array('dataid'=>$data->id,'userid'=>$user->id), 'timemodified DESC')) { 1095 data_print_template('singletemplate', $records, $data); 1096 } 1097 } 1098 1099 /** 1100 * Return grade for given user or all users. 1101 * 1102 * @global object 1103 * @param object $data 1104 * @param int $userid optional user id, 0 means all users 1105 * @return array array of grades, false if none 1106 */ 1107 function data_get_user_grades($data, $userid=0) { 1108 global $CFG; 1109 1110 require_once($CFG->dirroot.'/rating/lib.php'); 1111 1112 $ratingoptions = new stdClass; 1113 $ratingoptions->component = 'mod_data'; 1114 $ratingoptions->ratingarea = 'entry'; 1115 $ratingoptions->modulename = 'data'; 1116 $ratingoptions->moduleid = $data->id; 1117 1118 $ratingoptions->userid = $userid; 1119 $ratingoptions->aggregationmethod = $data->assessed; 1120 $ratingoptions->scaleid = $data->scale; 1121 $ratingoptions->itemtable = 'data_records'; 1122 $ratingoptions->itemtableusercolumn = 'userid'; 1123 1124 $rm = new rating_manager(); 1125 return $rm->get_user_grades($ratingoptions); 1126 } 1127 1128 /** 1129 * Update activity grades 1130 * 1131 * @category grade 1132 * @param object $data 1133 * @param int $userid specific user only, 0 means all 1134 * @param bool $nullifnone 1135 */ 1136 function data_update_grades($data, $userid=0, $nullifnone=true) { 1137 global $CFG, $DB; 1138 require_once($CFG->libdir.'/gradelib.php'); 1139 1140 if (!$data->assessed) { 1141 data_grade_item_update($data); 1142 1143 } else if ($grades = data_get_user_grades($data, $userid)) { 1144 data_grade_item_update($data, $grades); 1145 1146 } else if ($userid and $nullifnone) { 1147 $grade = new stdClass(); 1148 $grade->userid = $userid; 1149 $grade->rawgrade = NULL; 1150 data_grade_item_update($data, $grade); 1151 1152 } else { 1153 data_grade_item_update($data); 1154 } 1155 } 1156 1157 /** 1158 * Update/create grade item for given data 1159 * 1160 * @category grade 1161 * @param stdClass $data A database instance with extra cmidnumber property 1162 * @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook 1163 * @return object grade_item 1164 */ 1165 function data_grade_item_update($data, $grades=NULL) { 1166 global $CFG; 1167 require_once($CFG->libdir.'/gradelib.php'); 1168 1169 $params = array('itemname'=>$data->name, 'idnumber'=>$data->cmidnumber); 1170 1171 if (!$data->assessed or $data->scale == 0) { 1172 $params['gradetype'] = GRADE_TYPE_NONE; 1173 1174 } else if ($data->scale > 0) { 1175 $params['gradetype'] = GRADE_TYPE_VALUE; 1176 $params['grademax'] = $data->scale; 1177 $params['grademin'] = 0; 1178 1179 } else if ($data->scale < 0) { 1180 $params['gradetype'] = GRADE_TYPE_SCALE; 1181 $params['scaleid'] = -$data->scale; 1182 } 1183 1184 if ($grades === 'reset') { 1185 $params['reset'] = true; 1186 $grades = NULL; 1187 } 1188 1189 return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, $grades, $params); 1190 } 1191 1192 /** 1193 * Delete grade item for given data 1194 * 1195 * @category grade 1196 * @param object $data object 1197 * @return object grade_item 1198 */ 1199 function data_grade_item_delete($data) { 1200 global $CFG; 1201 require_once($CFG->libdir.'/gradelib.php'); 1202 1203 return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, NULL, array('deleted'=>1)); 1204 } 1205 1206 // junk functions 1207 /** 1208 * takes a list of records, the current data, a search string, 1209 * and mode to display prints the translated template 1210 * 1211 * @global object 1212 * @global object 1213 * @param string $template 1214 * @param array $records 1215 * @param object $data 1216 * @param string $search 1217 * @param int $page 1218 * @param bool $return 1219 * @param object $jumpurl a moodle_url by which to jump back to the record list (can be null) 1220 * @return mixed 1221 */ 1222 function data_print_template($template, $records, $data, $search='', $page=0, $return=false, moodle_url $jumpurl=null) { 1223 global $CFG, $DB, $OUTPUT; 1224 1225 $cm = get_coursemodule_from_instance('data', $data->id); 1226 $context = context_module::instance($cm->id); 1227 1228 static $fields = array(); 1229 static $dataid = null; 1230 1231 if (empty($dataid)) { 1232 $dataid = $data->id; 1233 } else if ($dataid != $data->id) { 1234 $fields = array(); 1235 } 1236 1237 if (empty($fields)) { 1238 $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id)); 1239 foreach ($fieldrecords as $fieldrecord) { 1240 $fields[]= data_get_field($fieldrecord, $data); 1241 } 1242 } 1243 1244 if (empty($records)) { 1245 return; 1246 } 1247 1248 if (!$jumpurl) { 1249 $jumpurl = new moodle_url('/mod/data/view.php', array('d' => $data->id)); 1250 } 1251 $jumpurl = new moodle_url($jumpurl, array('page' => $page, 'sesskey' => sesskey())); 1252 1253 foreach ($records as $record) { // Might be just one for the single template 1254 1255 // Replacing tags 1256 $patterns = array(); 1257 $replacement = array(); 1258 1259 // Then we generate strings to replace for normal tags 1260 foreach ($fields as $field) { 1261 $patterns[]='[['.$field->field->name.']]'; 1262 $replacement[] = highlight($search, $field->display_browse_field($record->id, $template)); 1263 } 1264 1265 $canmanageentries = has_capability('mod/data:manageentries', $context); 1266 1267 // Replacing special tags (##Edit##, ##Delete##, ##More##) 1268 $patterns[]='##edit##'; 1269 $patterns[]='##delete##'; 1270 if (data_user_can_manage_entry($record, $data, $context)) { 1271 $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/edit.php?d=' 1272 .$data->id.'&rid='.$record->id.'&sesskey='.sesskey().'"><img src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall" alt="'.get_string('edit').'" title="'.get_string('edit').'" /></a>'; 1273 $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/view.php?d=' 1274 .$data->id.'&delete='.$record->id.'&sesskey='.sesskey().'"><img src="'.$OUTPUT->pix_url('t/delete') . '" class="iconsmall" alt="'.get_string('delete').'" title="'.get_string('delete').'" /></a>'; 1275 } else { 1276 $replacement[] = ''; 1277 $replacement[] = ''; 1278 } 1279 1280 $moreurl = $CFG->wwwroot . '/mod/data/view.php?d=' . $data->id . '&rid=' . $record->id; 1281 if ($search) { 1282 $moreurl .= '&filter=1'; 1283 } 1284 $patterns[]='##more##'; 1285 $replacement[] = '<a href="'.$moreurl.'"><img src="'.$OUTPUT->pix_url('t/preview'). 1286 '" class="iconsmall" alt="'.get_string('more', 'data').'" title="'.get_string('more', 'data'). 1287 '" /></a>'; 1288 1289 $patterns[]='##moreurl##'; 1290 $replacement[] = $moreurl; 1291 1292 $patterns[]='##delcheck##'; 1293 if ($canmanageentries) { 1294 $replacement[] = html_writer::checkbox('delcheck[]', $record->id, false, '', array('class' => 'recordcheckbox')); 1295 } else { 1296 $replacement[] = ''; 1297 } 1298 1299 $patterns[]='##user##'; 1300 $replacement[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$record->userid. 1301 '&course='.$data->course.'">'.fullname($record).'</a>'; 1302 1303 $patterns[] = '##userpicture##'; 1304 $ruser = user_picture::unalias($record, null, 'userid'); 1305 $replacement[] = $OUTPUT->user_picture($ruser, array('courseid' => $data->course)); 1306 1307 $patterns[]='##export##'; 1308 1309 if (!empty($CFG->enableportfolios) && ($template == 'singletemplate' || $template == 'listtemplate') 1310 && ((has_capability('mod/data:exportentry', $context) 1311 || (data_isowner($record->id) && has_capability('mod/data:exportownentry', $context))))) { 1312 require_once($CFG->libdir . '/portfoliolib.php'); 1313 $button = new portfolio_add_button(); 1314 $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id, 'recordid' => $record->id), 'mod_data'); 1315 list($formats, $files) = data_portfolio_caller::formats($fields, $record); 1316 $button->set_formats($formats); 1317 $replacement[] = $button->to_html(PORTFOLIO_ADD_ICON_LINK); 1318 } else { 1319 $replacement[] = ''; 1320 } 1321 1322 $patterns[] = '##timeadded##'; 1323 $replacement[] = userdate($record->timecreated); 1324 1325 $patterns[] = '##timemodified##'; 1326 $replacement [] = userdate($record->timemodified); 1327 1328 $patterns[]='##approve##'; 1329 if (has_capability('mod/data:approve', $context) && ($data->approval) && (!$record->approved)) { 1330 $approveurl = new moodle_url($jumpurl, array('approve' => $record->id)); 1331 $approveicon = new pix_icon('t/approve', get_string('approve', 'data'), '', array('class' => 'iconsmall')); 1332 $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($approveurl, $approveicon), 1333 array('class' => 'approve')); 1334 } else { 1335 $replacement[] = ''; 1336 } 1337 1338 $patterns[]='##disapprove##'; 1339 if (has_capability('mod/data:approve', $context) && ($data->approval) && ($record->approved)) { 1340 $disapproveurl = new moodle_url($jumpurl, array('disapprove' => $record->id)); 1341 $disapproveicon = new pix_icon('t/block', get_string('disapprove', 'data'), '', array('class' => 'iconsmall')); 1342 $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($disapproveurl, $disapproveicon), 1343 array('class' => 'disapprove')); 1344 } else { 1345 $replacement[] = ''; 1346 } 1347 1348 $patterns[] = '##approvalstatus##'; 1349 if (!$data->approval) { 1350 $replacement[] = ''; 1351 } else if ($record->approved) { 1352 $replacement[] = get_string('approved', 'data'); 1353 } else { 1354 $replacement[] = get_string('notapproved', 'data'); 1355 } 1356 1357 $patterns[]='##comments##'; 1358 if (($template == 'listtemplate') && ($data->comments)) { 1359 1360 if (!empty($CFG->usecomments)) { 1361 require_once($CFG->dirroot . '/comment/lib.php'); 1362 list($context, $course, $cm) = get_context_info_array($context->id); 1363 $cmt = new stdClass(); 1364 $cmt->context = $context; 1365 $cmt->course = $course; 1366 $cmt->cm = $cm; 1367 $cmt->area = 'database_entry'; 1368 $cmt->itemid = $record->id; 1369 $cmt->showcount = true; 1370 $cmt->component = 'mod_data'; 1371 $comment = new comment($cmt); 1372 $replacement[] = $comment->output(true); 1373 } 1374 } else { 1375 $replacement[] = ''; 1376 } 1377 1378 // actual replacement of the tags 1379 $newtext = str_ireplace($patterns, $replacement, $data->{$template}); 1380 1381 // no more html formatting and filtering - see MDL-6635 1382 if ($return) { 1383 return $newtext; 1384 } else { 1385 echo $newtext; 1386 1387 // hack alert - return is always false in singletemplate anyway ;-) 1388 /********************************** 1389 * Printing Ratings Form * 1390 *********************************/ 1391 if ($template == 'singletemplate') { //prints ratings options 1392 data_print_ratings($data, $record); 1393 } 1394 1395 /********************************** 1396 * Printing Comments Form * 1397 *********************************/ 1398 if (($template == 'singletemplate') && ($data->comments)) { 1399 if (!empty($CFG->usecomments)) { 1400 require_once($CFG->dirroot . '/comment/lib.php'); 1401 list($context, $course, $cm) = get_context_info_array($context->id); 1402 $cmt = new stdClass(); 1403 $cmt->context = $context; 1404 $cmt->course = $course; 1405 $cmt->cm = $cm; 1406 $cmt->area = 'database_entry'; 1407 $cmt->itemid = $record->id; 1408 $cmt->showcount = true; 1409 $cmt->component = 'mod_data'; 1410 $comment = new comment($cmt); 1411 $comment->output(false); 1412 } 1413 } 1414 } 1415 } 1416 } 1417 1418 /** 1419 * Return rating related permissions 1420 * 1421 * @param string $contextid the context id 1422 * @param string $component the component to get rating permissions for 1423 * @param string $ratingarea the rating area to get permissions for 1424 * @return array an associative array of the user's rating permissions 1425 */ 1426 function data_rating_permissions($contextid, $component, $ratingarea) { 1427 $context = context::instance_by_id($contextid, MUST_EXIST); 1428 if ($component != 'mod_data' || $ratingarea != 'entry') { 1429 return null; 1430 } 1431 return array( 1432 'view' => has_capability('mod/data:viewrating',$context), 1433 'viewany' => has_capability('mod/data:viewanyrating',$context), 1434 'viewall' => has_capability('mod/data:viewallratings',$context), 1435 'rate' => has_capability('mod/data:rate',$context) 1436 ); 1437 } 1438 1439 /** 1440 * Validates a submitted rating 1441 * @param array $params submitted data 1442 * context => object the context in which the rated items exists [required] 1443 * itemid => int the ID of the object being rated 1444 * scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required] 1445 * rating => int the submitted rating 1446 * rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required] 1447 * aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required] 1448 * @return boolean true if the rating is valid. Will throw rating_exception if not 1449 */ 1450 function data_rating_validate($params) { 1451 global $DB, $USER; 1452 1453 // Check the component is mod_data 1454 if ($params['component'] != 'mod_data') { 1455 throw new rating_exception('invalidcomponent'); 1456 } 1457 1458 // Check the ratingarea is entry (the only rating area in data module) 1459 if ($params['ratingarea'] != 'entry') { 1460 throw new rating_exception('invalidratingarea'); 1461 } 1462 1463 // Check the rateduserid is not the current user .. you can't rate your own entries 1464 if ($params['rateduserid'] == $USER->id) { 1465 throw new rating_exception('nopermissiontorate'); 1466 } 1467 1468 $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 1469 FROM {data_records} r 1470 JOIN {data} d ON r.dataid = d.id 1471 WHERE r.id = :itemid"; 1472 $dataparams = array('itemid'=>$params['itemid']); 1473 if (!$info = $DB->get_record_sql($datasql, $dataparams)) { 1474 //item doesn't exist 1475 throw new rating_exception('invaliditemid'); 1476 } 1477 1478 if ($info->scale != $params['scaleid']) { 1479 //the scale being submitted doesnt match the one in the database 1480 throw new rating_exception('invalidscaleid'); 1481 } 1482 1483 //check that the submitted rating is valid for the scale 1484 1485 // lower limit 1486 if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) { 1487 throw new rating_exception('invalidnum'); 1488 } 1489 1490 // upper limit 1491 if ($info->scale < 0) { 1492 //its a custom scale 1493 $scalerecord = $DB->get_record('scale', array('id' => -$info->scale)); 1494 if ($scalerecord) { 1495 $scalearray = explode(',', $scalerecord->scale); 1496 if ($params['rating'] > count($scalearray)) { 1497 throw new rating_exception('invalidnum'); 1498 } 1499 } else { 1500 throw new rating_exception('invalidscaleid'); 1501 } 1502 } else if ($params['rating'] > $info->scale) { 1503 //if its numeric and submitted rating is above maximum 1504 throw new rating_exception('invalidnum'); 1505 } 1506 1507 if ($info->approval && !$info->approved) { 1508 //database requires approval but this item isnt approved 1509 throw new rating_exception('nopermissiontorate'); 1510 } 1511 1512 // check the item we're rating was created in the assessable time window 1513 if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) { 1514 if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) { 1515 throw new rating_exception('notavailable'); 1516 } 1517 } 1518 1519 $course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST); 1520 $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST); 1521 $context = context_module::instance($cm->id); 1522 1523 // if the supplied context doesnt match the item's context 1524 if ($context->id != $params['context']->id) { 1525 throw new rating_exception('invalidcontext'); 1526 } 1527 1528 // Make sure groups allow this user to see the item they're rating 1529 $groupid = $info->groupid; 1530 if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used 1531 if (!groups_group_exists($groupid)) { // Can't find group 1532 throw new rating_exception('cannotfindgroup');//something is wrong 1533 } 1534 1535 if (!groups_is_member($groupid) and !has_capability('moodle/site:accessallgroups', $context)) { 1536 // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS 1537 throw new rating_exception('notmemberofgroup'); 1538 } 1539 } 1540 1541 return true; 1542 } 1543 1544 /** 1545 * Can the current user see ratings for a given itemid? 1546 * 1547 * @param array $params submitted data 1548 * contextid => int contextid [required] 1549 * component => The component for this module - should always be mod_data [required] 1550 * ratingarea => object the context in which the rated items exists [required] 1551 * itemid => int the ID of the object being rated [required] 1552 * scaleid => int scale id [optional] 1553 * @return bool 1554 * @throws coding_exception 1555 * @throws rating_exception 1556 */ 1557 function mod_data_rating_can_see_item_ratings($params) { 1558 global $DB; 1559 1560 // Check the component is mod_data. 1561 if (!isset($params['component']) || $params['component'] != 'mod_data') { 1562 throw new rating_exception('invalidcomponent'); 1563 } 1564 1565 // Check the ratingarea is entry (the only rating area in data). 1566 if (!isset($params['ratingarea']) || $params['ratingarea'] != 'entry') { 1567 throw new rating_exception('invalidratingarea'); 1568 } 1569 1570 if (!isset($params['itemid'])) { 1571 throw new rating_exception('invaliditemid'); 1572 } 1573 1574 $datasql = "SELECT d.id as dataid, d.course, r.groupid 1575 FROM {data_records} r 1576 JOIN {data} d ON r.dataid = d.id 1577 WHERE r.id = :itemid"; 1578 $dataparams = array('itemid' => $params['itemid']); 1579 if (!$info = $DB->get_record_sql($datasql, $dataparams)) { 1580 // Item doesn't exist. 1581 throw new rating_exception('invaliditemid'); 1582 } 1583 1584 // User can see ratings of all participants. 1585 if ($info->groupid == 0) { 1586 return true; 1587 } 1588 1589 $course = $DB->get_record('course', array('id' => $info->course), '*', MUST_EXIST); 1590 $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST); 1591 1592 // Make sure groups allow this user to see the item they're rating. 1593 return groups_group_visible($info->groupid, $course, $cm); 1594 } 1595 1596 1597 /** 1598 * function that takes in the current data, number of items per page, 1599 * a search string and prints a preference box in view.php 1600 * 1601 * This preference box prints a searchable advanced search template if 1602 * a) A template is defined 1603 * b) The advanced search checkbox is checked. 1604 * 1605 * @global object 1606 * @global object 1607 * @param object $data 1608 * @param int $perpage 1609 * @param string $search 1610 * @param string $sort 1611 * @param string $order 1612 * @param array $search_array 1613 * @param int $advanced 1614 * @param string $mode 1615 * @return void 1616 */ 1617 function data_print_preference_form($data, $perpage, $search, $sort='', $order='ASC', $search_array = '', $advanced = 0, $mode= ''){ 1618 global $CFG, $DB, $PAGE, $OUTPUT; 1619 1620 $cm = get_coursemodule_from_instance('data', $data->id); 1621 $context = context_module::instance($cm->id); 1622 echo '<br /><div class="datapreferences">'; 1623 echo '<form id="options" action="view.php" method="get">'; 1624 echo '<div>'; 1625 echo '<input type="hidden" name="d" value="'.$data->id.'" />'; 1626 if ($mode =='asearch') { 1627 $advanced = 1; 1628 echo '<input type="hidden" name="mode" value="list" />'; 1629 } 1630 echo '<label for="pref_perpage">'.get_string('pagesize','data').'</label> '; 1631 $pagesizes = array(2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,15=>15, 1632 20=>20,30=>30,40=>40,50=>50,100=>100,200=>200,300=>300,400=>400,500=>500,1000=>1000); 1633 echo html_writer::select($pagesizes, 'perpage', $perpage, false, array('id'=>'pref_perpage')); 1634 1635 if ($advanced) { 1636 $regsearchclass = 'search_none'; 1637 $advancedsearchclass = 'search_inline'; 1638 } else { 1639 $regsearchclass = 'search_inline'; 1640 $advancedsearchclass = 'search_none'; 1641 } 1642 echo '<div id="reg_search" class="' . $regsearchclass . '" > '; 1643 echo '<label for="pref_search">'.get_string('search').'</label> <input type="text" size="16" name="search" id= "pref_search" value="'.s($search).'" /></div>'; 1644 echo ' <label for="pref_sortby">'.get_string('sortby').'</label> '; 1645 // foreach field, print the option 1646 echo '<select name="sort" id="pref_sortby">'; 1647 if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'name')) { 1648 echo '<optgroup label="'.get_string('fields', 'data').'">'; 1649 foreach ($fields as $field) { 1650 if ($field->id == $sort) { 1651 echo '<option value="'.$field->id.'" selected="selected">'.$field->name.'</option>'; 1652 } else { 1653 echo '<option value="'.$field->id.'">'.$field->name.'</option>'; 1654 } 1655 } 1656 echo '</optgroup>'; 1657 } 1658 $options = array(); 1659 $options[DATA_TIMEADDED] = get_string('timeadded', 'data'); 1660 $options[DATA_TIMEMODIFIED] = get_string('timemodified', 'data'); 1661 $options[DATA_FIRSTNAME] = get_string('authorfirstname', 'data'); 1662 $options[DATA_LASTNAME] = get_string('authorlastname', 'data'); 1663 if ($data->approval and has_capability('mod/data:approve', $context)) { 1664 $options[DATA_APPROVED] = get_string('approved', 'data'); 1665 } 1666 echo '<optgroup label="'.get_string('other', 'data').'">'; 1667 foreach ($options as $key => $name) { 1668 if ($key == $sort) { 1669 echo '<option value="'.$key.'" selected="selected">'.$name.'</option>'; 1670 } else { 1671 echo '<option value="'.$key.'">'.$name.'</option>'; 1672 } 1673 } 1674 echo '</optgroup>'; 1675 echo '</select>'; 1676 echo '<label for="pref_order" class="accesshide">'.get_string('order').'</label>'; 1677 echo '<select id="pref_order" name="order">'; 1678 if ($order == 'ASC') { 1679 echo '<option value="ASC" selected="selected">'.get_string('ascending','data').'</option>'; 1680 } else { 1681 echo '<option value="ASC">'.get_string('ascending','data').'</option>'; 1682 } 1683 if ($order == 'DESC') { 1684 echo '<option value="DESC" selected="selected">'.get_string('descending','data').'</option>'; 1685 } else { 1686 echo '<option value="DESC">'.get_string('descending','data').'</option>'; 1687 } 1688 echo '</select>'; 1689 1690 if ($advanced) { 1691 $checked = ' checked="checked" '; 1692 } 1693 else { 1694 $checked = ''; 1695 } 1696 $PAGE->requires->js('/mod/data/data.js'); 1697 echo ' <input type="hidden" name="advanced" value="0" />'; 1698 echo ' <input type="hidden" name="filter" value="1" />'; 1699 echo ' <input type="checkbox" id="advancedcheckbox" name="advanced" value="1" '.$checked.' onchange="showHideAdvSearch(this.checked);" /><label for="advancedcheckbox">'.get_string('advancedsearch', 'data').'</label>'; 1700 echo ' <input type="submit" value="'.get_string('savesettings','data').'" />'; 1701 1702 echo '<br />'; 1703 echo '<div class="' . $advancedsearchclass . '" id="data_adv_form">'; 1704 echo '<table class="boxaligncenter">'; 1705 1706 // print ASC or DESC 1707 echo '<tr><td colspan="2"> </td></tr>'; 1708 $i = 0; 1709 1710 // Determine if we are printing all fields for advanced search, or the template for advanced search 1711 // If a template is not defined, use the deafault template and display all fields. 1712 if(empty($data->asearchtemplate)) { 1713 data_generate_default_template($data, 'asearchtemplate'); 1714 } 1715 1716 static $fields = array(); 1717 static $dataid = null; 1718 1719 if (empty($dataid)) { 1720 $dataid = $data->id; 1721 } else if ($dataid != $data->id) { 1722 $fields = array(); 1723 } 1724 1725 if (empty($fields)) { 1726 $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id)); 1727 foreach ($fieldrecords as $fieldrecord) { 1728 $fields[]= data_get_field($fieldrecord, $data); 1729 } 1730 } 1731 1732 // Replacing tags 1733 $patterns = array(); 1734 $replacement = array(); 1735 1736 // Then we generate strings to replace for normal tags 1737 foreach ($fields as $field) { 1738 $fieldname = $field->field->name; 1739 $fieldname = preg_quote($fieldname, '/'); 1740 $patterns[] = "/\[\[$fieldname\]\]/i"; 1741 $searchfield = data_get_field_from_id($field->field->id, $data); 1742 if (!empty($search_array[$field->field->id]->data)) { 1743 $replacement[] = $searchfield->display_search_field($search_array[$field->field->id]->data); 1744 } else { 1745 $replacement[] = $searchfield->display_search_field(); 1746 } 1747 } 1748 $fn = !empty($search_array[DATA_FIRSTNAME]->data) ? $search_array[DATA_FIRSTNAME]->data : ''; 1749 $ln = !empty($search_array[DATA_LASTNAME]->data) ? $search_array[DATA_LASTNAME]->data : ''; 1750 $patterns[] = '/##firstname##/'; 1751 $replacement[] = '<label class="accesshide" for="u_fn">'.get_string('authorfirstname', 'data').'</label><input type="text" size="16" id="u_fn" name="u_fn" value="'.s($fn).'" />'; 1752 $patterns[] = '/##lastname##/'; 1753 $replacement[] = '<label class="accesshide" for="u_ln">'.get_string('authorlastname', 'data').'</label><input type="text" size="16" id="u_ln" name="u_ln" value="'.s($ln).'" />'; 1754 1755 // actual replacement of the tags 1756 $newtext = preg_replace($patterns, $replacement, $data->asearchtemplate); 1757 1758 $options = new stdClass(); 1759 $options->para=false; 1760 $options->noclean=true; 1761 echo '<tr><td>'; 1762 echo format_text($newtext, FORMAT_HTML, $options); 1763 echo '</td></tr>'; 1764 1765 echo '<tr><td colspan="4"><br/><input type="submit" value="'.get_string('savesettings','data').'" /><input type="submit" name="resetadv" value="'.get_string('resetsettings','data').'" /></td></tr>'; 1766 echo '</table>'; 1767 echo '</div>'; 1768 echo '</div>'; 1769 echo '</form>'; 1770 echo '</div>'; 1771 } 1772 1773 /** 1774 * @global object 1775 * @global object 1776 * @param object $data 1777 * @param object $record 1778 * @return void Output echo'd 1779 */ 1780 function data_print_ratings($data, $record) { 1781 global $OUTPUT; 1782 if (!empty($record->rating)){ 1783 echo $OUTPUT->render($record->rating); 1784 } 1785 } 1786 1787 /** 1788 * List the actions that correspond to a view of this module. 1789 * This is used by the participation report. 1790 * 1791 * Note: This is not used by new logging system. Event with 1792 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will 1793 * be considered as view action. 1794 * 1795 * @return array 1796 */ 1797 function data_get_view_actions() { 1798 return array('view'); 1799 } 1800 1801 /** 1802 * List the actions that correspond to a post of this module. 1803 * This is used by the participation report. 1804 * 1805 * Note: This is not used by new logging system. Event with 1806 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING 1807 * will be considered as post action. 1808 * 1809 * @return array 1810 */ 1811 function data_get_post_actions() { 1812 return array('add','update','record delete'); 1813 } 1814 1815 /** 1816 * @param string $name 1817 * @param int $dataid 1818 * @param int $fieldid 1819 * @return bool 1820 */ 1821 function data_fieldname_exists($name, $dataid, $fieldid = 0) { 1822 global $DB; 1823 1824 if (!is_numeric($name)) { 1825 $like = $DB->sql_like('df.name', ':name', false); 1826 } else { 1827 $like = "df.name = :name"; 1828 } 1829 $params = array('name'=>$name); 1830 if ($fieldid) { 1831 $params['dataid'] = $dataid; 1832 $params['fieldid1'] = $fieldid; 1833 $params['fieldid2'] = $fieldid; 1834 return $DB->record_exists_sql("SELECT * FROM {data_fields} df 1835 WHERE $like AND df.dataid = :dataid 1836 AND ((df.id < :fieldid1) OR (df.id > :fieldid2))", $params); 1837 } else { 1838 $params['dataid'] = $dataid; 1839 return $DB->record_exists_sql("SELECT * FROM {data_fields} df 1840 WHERE $like AND df.dataid = :dataid", $params); 1841 } 1842 } 1843 1844 /** 1845 * @param array $fieldinput 1846 */ 1847 function data_convert_arrays_to_strings(&$fieldinput) { 1848 foreach ($fieldinput as $key => $val) { 1849 if (is_array($val)) { 1850 $str = ''; 1851 foreach ($val as $inner) { 1852 $str .= $inner . ','; 1853 } 1854 $str = substr($str, 0, -1); 1855 1856 $fieldinput->$key = $str; 1857 } 1858 } 1859 } 1860 1861 1862 /** 1863 * Converts a database (module instance) to use the Roles System 1864 * 1865 * @global object 1866 * @global object 1867 * @uses CONTEXT_MODULE 1868 * @uses CAP_PREVENT 1869 * @uses CAP_ALLOW 1870 * @param object $data a data object with the same attributes as a record 1871 * from the data database table 1872 * @param int $datamodid the id of the data module, from the modules table 1873 * @param array $teacherroles array of roles that have archetype teacher 1874 * @param array $studentroles array of roles that have archetype student 1875 * @param array $guestroles array of roles that have archetype guest 1876 * @param int $cmid the course_module id for this data instance 1877 * @return boolean data module was converted or not 1878 */ 1879 function data_convert_to_roles($data, $teacherroles=array(), $studentroles=array(), $cmid=NULL) { 1880 global $CFG, $DB, $OUTPUT; 1881 1882 if (!isset($data->participants) && !isset($data->assesspublic) 1883 && !isset($data->groupmode)) { 1884 // We assume that this database has already been converted to use the 1885 // Roles System. above fields get dropped the data module has been 1886 // upgraded to use Roles. 1887 return false; 1888 } 1889 1890 if (empty($cmid)) { 1891 // We were not given the course_module id. Try to find it. 1892 if (!$cm = get_coursemodule_from_instance('data', $data->id)) { 1893 echo $OUTPUT->notification('Could not get the course module for the data'); 1894 return false; 1895 } else { 1896 $cmid = $cm->id; 1897 } 1898 } 1899 $context = context_module::instance($cmid); 1900 1901 1902 // $data->participants: 1903 // 1 - Only teachers can add entries 1904 // 3 - Teachers and students can add entries 1905 switch ($data->participants) { 1906 case 1: 1907 foreach ($studentroles as $studentrole) { 1908 assign_capability('mod/data:writeentry', CAP_PREVENT, $studentrole->id, $context->id); 1909 } 1910 foreach ($teacherroles as $teacherrole) { 1911 assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id); 1912 } 1913 break; 1914 case 3: 1915 foreach ($studentroles as $studentrole) { 1916 assign_capability('mod/data:writeentry', CAP_ALLOW, $studentrole->id, $context->id); 1917 } 1918 foreach ($teacherroles as $teacherrole) { 1919 assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id); 1920 } 1921 break; 1922 } 1923 1924 // $data->assessed: 1925 // 2 - Only teachers can rate posts 1926 // 1 - Everyone can rate posts 1927 // 0 - No one can rate posts 1928 switch ($data->assessed) { 1929 case 0: 1930 foreach ($studentroles as $studentrole) { 1931 assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id); 1932 } 1933 foreach ($teacherroles as $teacherrole) { 1934 assign_capability('mod/data:rate', CAP_PREVENT, $teacherrole->id, $context->id); 1935 } 1936 break; 1937 case 1: 1938 foreach ($studentroles as $studentrole) { 1939 assign_capability('mod/data:rate', CAP_ALLOW, $studentrole->id, $context->id); 1940 } 1941 foreach ($teacherroles as $teacherrole) { 1942 assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id); 1943 } 1944 break; 1945 case 2: 1946 foreach ($studentroles as $studentrole) { 1947 assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id); 1948 } 1949 foreach ($teacherroles as $teacherrole) { 1950 assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id); 1951 } 1952 break; 1953 } 1954 1955 // $data->assesspublic: 1956 // 0 - Students can only see their own ratings 1957 // 1 - Students can see everyone's ratings 1958 switch ($data->assesspublic) { 1959 case 0: 1960 foreach ($studentroles as $studentrole) { 1961 assign_capability('mod/data:viewrating', CAP_PREVENT, $studentrole->id, $context->id); 1962 } 1963 foreach ($teacherroles as $teacherrole) { 1964 assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id); 1965 } 1966 break; 1967 case 1: 1968 foreach ($studentroles as $studentrole) { 1969 assign_capability('mod/data:viewrating', CAP_ALLOW, $studentrole->id, $context->id); 1970 } 1971 foreach ($teacherroles as $teacherrole) { 1972 assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id); 1973 } 1974 break; 1975 } 1976 1977 if (empty($cm)) { 1978 $cm = $DB->get_record('course_modules', array('id'=>$cmid)); 1979 } 1980 1981 switch ($cm->groupmode) { 1982 case NOGROUPS: 1983 break; 1984 case SEPARATEGROUPS: 1985 foreach ($studentroles as $studentrole) { 1986 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id); 1987 } 1988 foreach ($teacherroles as $teacherrole) { 1989 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id); 1990 } 1991 break; 1992 case VISIBLEGROUPS: 1993 foreach ($studentroles as $studentrole) { 1994 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id); 1995 } 1996 foreach ($teacherroles as $teacherrole) { 1997 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id); 1998 } 1999 break; 2000 } 2001 return true; 2002 } 2003 2004 /** 2005 * Returns the best name to show for a preset 2006 * 2007 * @param string $shortname 2008 * @param string $path 2009 * @return string 2010 */ 2011 function data_preset_name($shortname, $path) { 2012 2013 // We are looking inside the preset itself as a first choice, but also in normal data directory 2014 $string = get_string('modulename', 'datapreset_'.$shortname); 2015 2016 if (substr($string, 0, 1) == '[') { 2017 return $shortname; 2018 } else { 2019 return $string; 2020 } 2021 } 2022 2023 /** 2024 * Returns an array of all the available presets. 2025 * 2026 * @return array 2027 */ 2028 function data_get_available_presets($context) { 2029 global $CFG, $USER; 2030 2031 $presets = array(); 2032 2033 // First load the ratings sub plugins that exist within the modules preset dir 2034 if ($dirs = core_component::get_plugin_list('datapreset')) { 2035 foreach ($dirs as $dir=>$fulldir) { 2036 if (is_directory_a_preset($fulldir)) { 2037 $preset = new stdClass(); 2038 $preset->path = $fulldir; 2039 $preset->userid = 0; 2040 $preset->shortname = $dir; 2041 $preset->name = data_preset_name($dir, $fulldir); 2042 if (file_exists($fulldir.'/screenshot.jpg')) { 2043 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.jpg'; 2044 } else if (file_exists($fulldir.'/screenshot.png')) { 2045 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.png'; 2046 } else if (file_exists($fulldir.'/screenshot.gif')) { 2047 $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.gif'; 2048 } 2049 $presets[] = $preset; 2050 } 2051 } 2052 } 2053 // Now add to that the site presets that people have saved 2054 $presets = data_get_available_site_presets($context, $presets); 2055 return $presets; 2056 } 2057 2058 /** 2059 * Gets an array of all of the presets that users have saved to the site. 2060 * 2061 * @param stdClass $context The context that we are looking from. 2062 * @param array $presets 2063 * @return array An array of presets 2064 */ 2065 function data_get_available_site_presets($context, array $presets=array()) { 2066 global $USER; 2067 2068 $fs = get_file_storage(); 2069 $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA); 2070 $canviewall = has_capability('mod/data:viewalluserpresets', $context); 2071 if (empty($files)) { 2072 return $presets; 2073 } 2074 foreach ($files as $file) { 2075 if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory() || (!$canviewall && $file->get_userid() != $USER->id)) { 2076 continue; 2077 } 2078 $preset = new stdClass; 2079 $preset->path = $file->get_filepath(); 2080 $preset->name = trim($preset->path, '/'); 2081 $preset->shortname = $preset->name; 2082 $preset->userid = $file->get_userid(); 2083 $preset->id = $file->get_id(); 2084 $preset->storedfile = $file; 2085 $presets[] = $preset; 2086 } 2087 return $presets; 2088 } 2089 2090 /** 2091 * Deletes a saved preset. 2092 * 2093 * @param string $name 2094 * @return bool 2095 */ 2096 function data_delete_site_preset($name) { 2097 $fs = get_file_storage(); 2098 2099 $files = $fs->get_directory_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/'); 2100 if (!empty($files)) { 2101 foreach ($files as $file) { 2102 $file->delete(); 2103 } 2104 } 2105 2106 $dir = $fs->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/', '.'); 2107 if (!empty($dir)) { 2108 $dir->delete(); 2109 } 2110 return true; 2111 } 2112 2113 /** 2114 * Prints the heads for a page 2115 * 2116 * @param stdClass $course 2117 * @param stdClass $cm 2118 * @param stdClass $data 2119 * @param string $currenttab 2120 */ 2121 function data_print_header($course, $cm, $data, $currenttab='') { 2122 2123 global $CFG, $displaynoticegood, $displaynoticebad, $OUTPUT, $PAGE; 2124 2125 $PAGE->set_title($data->name); 2126 echo $OUTPUT->header(); 2127 echo $OUTPUT->heading(format_string($data->name), 2); 2128 echo $OUTPUT->box(format_module_intro('data', $data, $cm->id), 'generalbox', 'intro'); 2129 2130 // Groups needed for Add entry tab 2131 $currentgroup = groups_get_activity_group($cm); 2132 $groupmode = groups_get_activity_groupmode($cm); 2133 2134 // Print the tabs 2135 2136 if ($currenttab) { 2137 include ('tabs.php'); 2138 } 2139 2140 // Print any notices 2141 2142 if (!empty($displaynoticegood)) { 2143 echo $OUTPUT->notification($displaynoticegood, 'notifysuccess'); // good (usually green) 2144 } else if (!empty($displaynoticebad)) { 2145 echo $OUTPUT->notification($displaynoticebad); // bad (usuually red) 2146 } 2147 } 2148 2149 /** 2150 * Can user add more entries? 2151 * 2152 * @param object $data 2153 * @param mixed $currentgroup 2154 * @param int $groupmode 2155 * @param stdClass $context 2156 * @return bool 2157 */ 2158 function data_user_can_add_entry($data, $currentgroup, $groupmode, $context = null) { 2159 global $USER; 2160 2161 if (empty($context)) { 2162 $cm = get_coursemodule_from_instance('data', $data->id, 0, false, MUST_EXIST); 2163 $context = context_module::instance($cm->id); 2164 } 2165 2166 if (has_capability('mod/data:manageentries', $context)) { 2167 // no entry limits apply if user can manage 2168 2169 } else if (!has_capability('mod/data:writeentry', $context)) { 2170 return false; 2171 2172 } else if (data_atmaxentries($data)) { 2173 return false; 2174 } else if (data_in_readonly_period($data)) { 2175 // Check whether we're in a read-only period 2176 return false; 2177 } 2178 2179 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) { 2180 return true; 2181 } 2182 2183 if ($currentgroup) { 2184 return groups_is_member($currentgroup); 2185 } else { 2186 //else it might be group 0 in visible mode 2187 if ($groupmode == VISIBLEGROUPS){ 2188 return true; 2189 } else { 2190 return false; 2191 } 2192 } 2193 } 2194 2195 /** 2196 * Check whether the current user is allowed to manage the given record considering manageentries capability, 2197 * data_in_readonly_period() result, ownership (determined by data_isowner()) and manageapproved setting. 2198 * @param mixed $record record object or id 2199 * @param object $data data object 2200 * @param object $context context object 2201 * @return bool returns true if the user is allowd to edit the entry, false otherwise 2202 */ 2203 function data_user_can_manage_entry($record, $data, $context) { 2204 global $DB; 2205 2206 if (has_capability('mod/data:manageentries', $context)) { 2207 return true; 2208 } 2209 2210 // Check whether this activity is read-only at present. 2211 $readonly = data_in_readonly_period($data); 2212 2213 if (!$readonly) { 2214 // Get record object from db if just id given like in data_isowner. 2215 // ...done before calling data_isowner() to avoid querying db twice. 2216 if (!is_object($record)) { 2217 if (!$record = $DB->get_record('data_records', array('id' => $record))) { 2218 return false; 2219 } 2220 } 2221 if (data_isowner($record)) { 2222 if ($data->approval && $record->approved) { 2223 return $data->manageapproved == 1; 2224 } else { 2225 return true; 2226 } 2227 } 2228 } 2229 2230 return false; 2231 } 2232 2233 /** 2234 * Check whether the specified database activity is currently in a read-only period 2235 * 2236 * @param object $data 2237 * @return bool returns true if the time fields in $data indicate a read-only period; false otherwise 2238 */ 2239 function data_in_readonly_period($data) { 2240 $now = time(); 2241 if (!$data->timeviewfrom && !$data->timeviewto) { 2242 return false; 2243 } else if (($data->timeviewfrom && $now < $data->timeviewfrom) || ($data->timeviewto && $now > $data->timeviewto)) { 2244 return false; 2245 } 2246 return true; 2247 } 2248 2249 /** 2250 * @return bool 2251 */ 2252 function is_directory_a_preset($directory) { 2253 $directory = rtrim($directory, '/\\') . '/'; 2254 $status = file_exists($directory.'singletemplate.html') && 2255 file_exists($directory.'listtemplate.html') && 2256 file_exists($directory.'listtemplateheader.html') && 2257 file_exists($directory.'listtemplatefooter.html') && 2258 file_exists($directory.'addtemplate.html') && 2259 file_exists($directory.'rsstemplate.html') && 2260 file_exists($directory.'rsstitletemplate.html') && 2261 file_exists($directory.'csstemplate.css') && 2262 file_exists($directory.'jstemplate.js') && 2263 file_exists($directory.'preset.xml'); 2264 2265 return $status; 2266 } 2267 2268 /** 2269 * Abstract class used for data preset importers 2270 */ 2271 abstract class data_preset_importer { 2272 2273 protected $course; 2274 protected $cm; 2275 protected $module; 2276 protected $directory; 2277 2278 /** 2279 * Constructor 2280 * 2281 * @param stdClass $course 2282 * @param stdClass $cm 2283 * @param stdClass $module 2284 * @param string $directory 2285 */ 2286 public function __construct($course, $cm, $module, $directory) { 2287 $this->course = $course; 2288 $this->cm = $cm; 2289 $this->module = $module; 2290 $this->directory = $directory; 2291 } 2292 2293 /** 2294 * Returns the name of the directory the preset is located in 2295 * @return string 2296 */ 2297 public function get_directory() { 2298 return basename($this->directory); 2299 } 2300 2301 /** 2302 * Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage 2303 * @param file_storage $filestorage. should be null if using a conventional directory 2304 * @param stored_file $fileobj the directory to look in. null if using a conventional directory 2305 * @param string $dir the directory to look in. null if using the Moodle file storage 2306 * @param string $filename the name of the file we want 2307 * @return string the contents of the file or null if the file doesn't exist. 2308 */ 2309 public function data_preset_get_file_contents(&$filestorage, &$fileobj, $dir, $filename) { 2310 if(empty($filestorage) || empty($fileobj)) { 2311 if (substr($dir, -1)!='/') { 2312 $dir .= '/'; 2313 } 2314 if (file_exists($dir.$filename)) { 2315 return file_get_contents($dir.$filename); 2316 } else { 2317 return null; 2318 } 2319 } else { 2320 if ($filestorage->file_exists(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename)) { 2321 $file = $filestorage->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename); 2322 return $file->get_content(); 2323 } else { 2324 return null; 2325 } 2326 } 2327 2328 } 2329 /** 2330 * Gets the preset settings 2331 * @global moodle_database $DB 2332 * @return stdClass 2333 */ 2334 public function get_preset_settings() { 2335 global $DB; 2336 2337 $fs = $fileobj = null; 2338 if (!is_directory_a_preset($this->directory)) { 2339 //maybe the user requested a preset stored in the Moodle file storage 2340 2341 $fs = get_file_storage(); 2342 $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA); 2343 2344 //preset name to find will be the final element of the directory 2345 $explodeddirectory = explode('/', $this->directory); 2346 $presettofind = end($explodeddirectory); 2347 2348 //now go through the available files available and see if we can find it 2349 foreach ($files as $file) { 2350 if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory()) { 2351 continue; 2352 } 2353 $presetname = trim($file->get_filepath(), '/'); 2354 if ($presetname==$presettofind) { 2355 $this->directory = $presetname; 2356 $fileobj = $file; 2357 } 2358 } 2359 2360 if (empty($fileobj)) { 2361 print_error('invalidpreset', 'data', '', $this->directory); 2362 } 2363 } 2364 2365 $allowed_settings = array( 2366 'intro', 2367 'comments', 2368 'requiredentries', 2369 'requiredentriestoview', 2370 'maxentries', 2371 'rssarticles', 2372 'approval', 2373 'defaultsortdir', 2374 'defaultsort'); 2375 2376 $result = new stdClass; 2377 $result->settings = new stdClass; 2378 $result->importfields = array(); 2379 $result->currentfields = $DB->get_records('data_fields', array('dataid'=>$this->module->id)); 2380 if (!$result->currentfields) { 2381 $result->currentfields = array(); 2382 } 2383 2384 2385 /* Grab XML */ 2386 $presetxml = $this->data_preset_get_file_contents($fs, $fileobj, $this->directory,'preset.xml'); 2387 $parsedxml = xmlize($presetxml, 0); 2388 2389 /* First, do settings. Put in user friendly array. */ 2390 $settingsarray = $parsedxml['preset']['#']['settings'][0]['#']; 2391 $result->settings = new StdClass(); 2392 foreach ($settingsarray as $setting => $value) { 2393 if (!is_array($value) || !in_array($setting, $allowed_settings)) { 2394 // unsupported setting 2395 continue; 2396 } 2397 $result->settings->$setting = $value[0]['#']; 2398 } 2399 2400 /* Now work out fields to user friendly array */ 2401 $fieldsarray = $parsedxml['preset']['#']['field']; 2402 foreach ($fieldsarray as $field) { 2403 if (!is_array($field)) { 2404 continue; 2405 } 2406 $f = new StdClass(); 2407 foreach ($field['#'] as $param => $value) { 2408 if (!is_array($value)) { 2409 continue; 2410 } 2411 $f->$param = $value[0]['#']; 2412 } 2413 $f->dataid = $this->module->id; 2414 $f->type = clean_param($f->type, PARAM_ALPHA); 2415 $result->importfields[] = $f; 2416 } 2417 /* Now add the HTML templates to the settings array so we can update d */ 2418 $result->settings->singletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"singletemplate.html"); 2419 $result->settings->listtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplate.html"); 2420 $result->settings->listtemplateheader = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplateheader.html"); 2421 $result->settings->listtemplatefooter = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplatefooter.html"); 2422 $result->settings->addtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"addtemplate.html"); 2423 $result->settings->rsstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstemplate.html"); 2424 $result->settings->rsstitletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstitletemplate.html"); 2425 $result->settings->csstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"csstemplate.css"); 2426 $result->settings->jstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"jstemplate.js"); 2427 $result->settings->asearchtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"asearchtemplate.html"); 2428 2429 $result->settings->instance = $this->module->id; 2430 return $result; 2431 } 2432 2433 /** 2434 * Import the preset into the given database module 2435 * @return bool 2436 */ 2437 function import($overwritesettings) { 2438 global $DB, $CFG; 2439 2440 $params = $this->get_preset_settings(); 2441 $settings = $params->settings; 2442 $newfields = $params->importfields; 2443 $currentfields = $params->currentfields; 2444 $preservedfields = array(); 2445 2446 /* Maps fields and makes new ones */ 2447 if (!empty($newfields)) { 2448 /* We require an injective mapping, and need to know what to protect */ 2449 foreach ($newfields as $nid => $newfield) { 2450 $cid = optional_param("field_$nid", -1, PARAM_INT); 2451 if ($cid == -1) { 2452 continue; 2453 } 2454 if (array_key_exists($cid, $preservedfields)){ 2455 print_error('notinjectivemap', 'data'); 2456 } 2457 else $preservedfields[$cid] = true; 2458 } 2459 2460 foreach ($newfields as $nid => $newfield) { 2461 $cid = optional_param("field_$nid", -1, PARAM_INT); 2462 2463 /* A mapping. Just need to change field params. Data kept. */ 2464 if ($cid != -1 and isset($currentfields[$cid])) { 2465 $fieldobject = data_get_field_from_id($currentfields[$cid]->id, $this->module); 2466 foreach ($newfield as $param => $value) { 2467 if ($param != "id") { 2468 $fieldobject->field->$param = $value; 2469 } 2470 } 2471 unset($fieldobject->field->similarfield); 2472 $fieldobject->update_field(); 2473 unset($fieldobject); 2474 } else { 2475 /* Make a new field */ 2476 include_once("field/$newfield->type/field.class.php"); 2477 2478 if (!isset($newfield->description)) { 2479 $newfield->description = ''; 2480 } 2481 $classname = 'data_field_'.$newfield->type; 2482 $fieldclass = new $classname($newfield, $this->module); 2483 $fieldclass->insert_field(); 2484 unset($fieldclass); 2485 } 2486 } 2487 } 2488 2489 /* Get rid of all old unused data */ 2490 if (!empty($preservedfields)) { 2491 foreach ($currentfields as $cid => $currentfield) { 2492 if (!array_key_exists($cid, $preservedfields)) { 2493 /* Data not used anymore so wipe! */ 2494 print "Deleting field $currentfield->name<br />"; 2495 2496 $id = $currentfield->id; 2497 //Why delete existing data records and related comments/ratings?? 2498 $DB->delete_records('data_content', array('fieldid'=>$id)); 2499 $DB->delete_records('data_fields', array('id'=>$id)); 2500 } 2501 } 2502 } 2503 2504 // handle special settings here 2505 if (!empty($settings->defaultsort)) { 2506 if (is_numeric($settings->defaultsort)) { 2507 // old broken value 2508 $settings->defaultsort = 0; 2509 } else { 2510 $settings->defaultsort = (int)$DB->get_field('data_fields', 'id', array('dataid'=>$this->module->id, 'name'=>$settings->defaultsort)); 2511 } 2512 } else { 2513 $settings->defaultsort = 0; 2514 } 2515 2516 // do we want to overwrite all current database settings? 2517 if ($overwritesettings) { 2518 // all supported settings 2519 $overwrite = array_keys((array)$settings); 2520 } else { 2521 // only templates and sorting 2522 $overwrite = array('singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter', 2523 'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate', 2524 'asearchtemplate', 'defaultsortdir', 'defaultsort'); 2525 } 2526 2527 // now overwrite current data settings 2528 foreach ($this->module as $prop=>$unused) { 2529 if (in_array($prop, $overwrite)) { 2530 $this->module->$prop = $settings->$prop; 2531 } 2532 } 2533 2534 data_update_instance($this->module); 2535 2536 return $this->cleanup(); 2537 } 2538 2539 /** 2540 * Any clean up routines should go here 2541 * @return bool 2542 */ 2543 public function cleanup() { 2544 return true; 2545 } 2546 } 2547 2548 /** 2549 * Data preset importer for uploaded presets 2550 */ 2551 class data_preset_upload_importer extends data_preset_importer { 2552 public function __construct($course, $cm, $module, $filepath) { 2553 global $USER; 2554 if (is_file($filepath)) { 2555 $fp = get_file_packer(); 2556 if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) { 2557 fulldelete($filepath); 2558 } 2559 $filepath .= '_extracted'; 2560 } 2561 parent::__construct($course, $cm, $module, $filepath); 2562 } 2563 public function cleanup() { 2564 return fulldelete($this->directory); 2565 } 2566 } 2567 2568 /** 2569 * Data preset importer for existing presets 2570 */ 2571 class data_preset_existing_importer extends data_preset_importer { 2572 protected $userid; 2573 public function __construct($course, $cm, $module, $fullname) { 2574 global $USER; 2575 list($userid, $shortname) = explode('/', $fullname, 2); 2576 $context = context_module::instance($cm->id); 2577 if ($userid && ($userid != $USER->id) && !has_capability('mod/data:manageuserpresets', $context) && !has_capability('mod/data:viewalluserpresets', $context)) { 2578 throw new coding_exception('Invalid preset provided'); 2579 } 2580 2581 $this->userid = $userid; 2582 $filepath = data_preset_path($course, $userid, $shortname); 2583 parent::__construct($course, $cm, $module, $filepath); 2584 } 2585 public function get_userid() { 2586 return $this->userid; 2587 } 2588 } 2589 2590 /** 2591 * @global object 2592 * @global object 2593 * @param object $course 2594 * @param int $userid 2595 * @param string $shortname 2596 * @return string 2597 */ 2598 function data_preset_path($course, $userid, $shortname) { 2599 global $USER, $CFG; 2600 2601 $context = context_course::instance($course->id); 2602 2603 $userid = (int)$userid; 2604 2605 $path = null; 2606 if ($userid > 0 && ($userid == $USER->id || has_capability('mod/data:viewalluserpresets', $context))) { 2607 $path = $CFG->dataroot.'/data/preset/'.$userid.'/'.$shortname; 2608 } else if ($userid == 0) { 2609 $path = $CFG->dirroot.'/mod/data/preset/'.$shortname; 2610 } else if ($userid < 0) { 2611 $path = $CFG->tempdir.'/data/'.-$userid.'/'.$shortname; 2612 } 2613 2614 return $path; 2615 } 2616 2617 /** 2618 * Implementation of the function for printing the form elements that control 2619 * whether the course reset functionality affects the data. 2620 * 2621 * @param $mform form passed by reference 2622 */ 2623 function data_reset_course_form_definition(&$mform) { 2624 $mform->addElement('header', 'dataheader', get_string('modulenameplural', 'data')); 2625 $mform->addElement('checkbox', 'reset_data', get_string('deleteallentries','data')); 2626 2627 $mform->addElement('checkbox', 'reset_data_notenrolled', get_string('deletenotenrolled', 'data')); 2628 $mform->disabledIf('reset_data_notenrolled', 'reset_data', 'checked'); 2629 2630 $mform->addElement('checkbox', 'reset_data_ratings', get_string('deleteallratings')); 2631 $mform->disabledIf('reset_data_ratings', 'reset_data', 'checked'); 2632 2633 $mform->addElement('checkbox', 'reset_data_comments', get_string('deleteallcomments')); 2634 $mform->disabledIf('reset_data_comments', 'reset_data', 'checked'); 2635 } 2636 2637 /** 2638 * Course reset form defaults. 2639 * @return array 2640 */ 2641 function data_reset_course_form_defaults($course) { 2642 return array('reset_data'=>0, 'reset_data_ratings'=>1, 'reset_data_comments'=>1, 'reset_data_notenrolled'=>0); 2643 } 2644 2645 /** 2646 * Removes all grades from gradebook 2647 * 2648 * @global object 2649 * @global object 2650 * @param int $courseid 2651 * @param string $type optional type 2652 */ 2653 function data_reset_gradebook($courseid, $type='') { 2654 global $CFG, $DB; 2655 2656 $sql = "SELECT d.*, cm.idnumber as cmidnumber, d.course as courseid 2657 FROM {data} d, {course_modules} cm, {modules} m 2658 WHERE m.name='data' AND m.id=cm.module AND cm.instance=d.id AND d.course=?"; 2659 2660 if ($datas = $DB->get_records_sql($sql, array($courseid))) { 2661 foreach ($datas as $data) { 2662 data_grade_item_update($data, 'reset'); 2663 } 2664 } 2665 } 2666 2667 /** 2668 * Actual implementation of the reset course functionality, delete all the 2669 * data responses for course $data->courseid. 2670 * 2671 * @global object 2672 * @global object 2673 * @param object $data the data submitted from the reset course. 2674 * @return array status array 2675 */ 2676 function data_reset_userdata($data) { 2677 global $CFG, $DB; 2678 require_once($CFG->libdir.'/filelib.php'); 2679 require_once($CFG->dirroot.'/rating/lib.php'); 2680 2681 $componentstr = get_string('modulenameplural', 'data'); 2682 $status = array(); 2683 2684 $allrecordssql = "SELECT r.id 2685 FROM {data_records} r 2686 INNER JOIN {data} d ON r.dataid = d.id 2687 WHERE d.course = ?"; 2688 2689 $alldatassql = "SELECT d.id 2690 FROM {data} d 2691 WHERE d.course=?"; 2692 2693 $rm = new rating_manager(); 2694 $ratingdeloptions = new stdClass; 2695 $ratingdeloptions->component = 'mod_data'; 2696 $ratingdeloptions->ratingarea = 'entry'; 2697 2698 // Set the file storage - may need it to remove files later. 2699 $fs = get_file_storage(); 2700 2701 // delete entries if requested 2702 if (!empty($data->reset_data)) { 2703 $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid)); 2704 $DB->delete_records_select('data_content', "recordid IN ($allrecordssql)", array($data->courseid)); 2705 $DB->delete_records_select('data_records', "dataid IN ($alldatassql)", array($data->courseid)); 2706 2707 if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) { 2708 foreach ($datas as $dataid=>$unused) { 2709 if (!$cm = get_coursemodule_from_instance('data', $dataid)) { 2710 continue; 2711 } 2712 $datacontext = context_module::instance($cm->id); 2713 2714 // Delete any files that may exist. 2715 $fs->delete_area_files($datacontext->id, 'mod_data', 'content'); 2716 2717 $ratingdeloptions->contextid = $datacontext->id; 2718 $rm->delete_ratings($ratingdeloptions); 2719 } 2720 } 2721 2722 if (empty($data->reset_gradebook_grades)) { 2723 // remove all grades from gradebook 2724 data_reset_gradebook($data->courseid); 2725 } 2726 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallentries', 'data'), 'error'=>false); 2727 } 2728 2729 // remove entries by users not enrolled into course 2730 if (!empty($data->reset_data_notenrolled)) { 2731 $recordssql = "SELECT r.id, r.userid, r.dataid, u.id AS userexists, u.deleted AS userdeleted 2732 FROM {data_records} r 2733 JOIN {data} d ON r.dataid = d.id 2734 LEFT JOIN {user} u ON r.userid = u.id 2735 WHERE d.course = ? AND r.userid > 0"; 2736 2737 $course_context = context_course::instance($data->courseid); 2738 $notenrolled = array(); 2739 $fields = array(); 2740 $rs = $DB->get_recordset_sql($recordssql, array($data->courseid)); 2741 foreach ($rs as $record) { 2742 if (array_key_exists($record->userid, $notenrolled) or !$record->userexists or $record->userdeleted 2743 or !is_enrolled($course_context, $record->userid)) { 2744 //delete ratings 2745 if (!$cm = get_coursemodule_from_instance('data', $record->dataid)) { 2746 continue; 2747 } 2748 $datacontext = context_module::instance($cm->id); 2749 $ratingdeloptions->contextid = $datacontext->id; 2750 $ratingdeloptions->itemid = $record->id; 2751 $rm->delete_ratings($ratingdeloptions); 2752 2753 // Delete any files that may exist. 2754 if ($contents = $DB->get_records('data_content', array('recordid' => $record->id), '', 'id')) { 2755 foreach ($contents as $content) { 2756 $fs->delete_area_files($datacontext->id, 'mod_data', 'content', $content->id); 2757 } 2758 } 2759 $notenrolled[$record->userid] = true; 2760 2761 $DB->delete_records('comments', array('itemid' => $record->id, 'commentarea' => 'database_entry')); 2762 $DB->delete_records('data_content', array('recordid' => $record->id)); 2763 $DB->delete_records('data_records', array('id' => $record->id)); 2764 } 2765 } 2766 $rs->close(); 2767 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'data'), 'error'=>false); 2768 } 2769 2770 // remove all ratings 2771 if (!empty($data->reset_data_ratings)) { 2772 if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) { 2773 foreach ($datas as $dataid=>$unused) { 2774 if (!$cm = get_coursemodule_from_instance('data', $dataid)) { 2775 continue; 2776 } 2777 $datacontext = context_module::instance($cm->id); 2778 2779 $ratingdeloptions->contextid = $datacontext->id; 2780 $rm->delete_ratings($ratingdeloptions); 2781 } 2782 } 2783 2784 if (empty($data->reset_gradebook_grades)) { 2785 // remove all grades from gradebook 2786 data_reset_gradebook($data->courseid); 2787 } 2788 2789 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false); 2790 } 2791 2792 // remove all comments 2793 if (!empty($data->reset_data_comments)) { 2794 $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid)); 2795 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false); 2796 } 2797 2798 // updating dates - shift may be negative too 2799 if ($data->timeshift) { 2800 shift_course_mod_dates('data', array('timeavailablefrom', 'timeavailableto', 'timeviewfrom', 'timeviewto'), $data->timeshift, $data->courseid); 2801 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false); 2802 } 2803 2804 return $status; 2805 } 2806 2807 /** 2808 * Returns all other caps used in module 2809 * 2810 * @return array 2811 */ 2812 function data_get_extra_capabilities() { 2813 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'); 2814 } 2815 2816 /** 2817 * @param string $feature FEATURE_xx constant for requested feature 2818 * @return mixed True if module supports feature, null if doesn't know 2819 */ 2820 function data_supports($feature) { 2821 switch($feature) { 2822 case FEATURE_GROUPS: return true; 2823 case FEATURE_GROUPINGS: return true; 2824 case FEATURE_MOD_INTRO: return true; 2825 case FEATURE_COMPLETION_TRACKS_VIEWS: return true; 2826 case FEATURE_GRADE_HAS_GRADE: return true; 2827 case FEATURE_GRADE_OUTCOMES: return true; 2828 case FEATURE_RATE: return true; 2829 case FEATURE_BACKUP_MOODLE2: return true; 2830 case FEATURE_SHOW_DESCRIPTION: return true; 2831 2832 default: return null; 2833 } 2834 } 2835 /** 2836 * @global object 2837 * @param array $export 2838 * @param string $delimiter_name 2839 * @param object $database 2840 * @param int $count 2841 * @param bool $return 2842 * @return string|void 2843 */ 2844 function data_export_csv($export, $delimiter_name, $database, $count, $return=false) { 2845 global $CFG; 2846 require_once($CFG->libdir . '/csvlib.class.php'); 2847 2848 $filename = $database . '-' . $count . '-record'; 2849 if ($count > 1) { 2850 $filename .= 's'; 2851 } 2852 if ($return) { 2853 return csv_export_writer::print_array($export, $delimiter_name, '"', true); 2854 } else { 2855 csv_export_writer::download_array($filename, $export, $delimiter_name); 2856 } 2857 } 2858 2859 /** 2860 * @global object 2861 * @param array $export 2862 * @param string $dataname 2863 * @param int $count 2864 * @return string 2865 */ 2866 function data_export_xls($export, $dataname, $count) { 2867 global $CFG; 2868 require_once("$CFG->libdir/excellib.class.php"); 2869 $filename = clean_filename("{$dataname}-{$count}_record"); 2870 if ($count > 1) { 2871 $filename .= 's'; 2872 } 2873 $filename .= clean_filename('-' . gmdate("Ymd_Hi")); 2874 $filename .= '.xls'; 2875 2876 $filearg = '-'; 2877 $workbook = new MoodleExcelWorkbook($filearg); 2878 $workbook->send($filename); 2879 $worksheet = array(); 2880 $worksheet[0] = $workbook->add_worksheet(''); 2881 $rowno = 0; 2882 foreach ($export as $row) { 2883 $colno = 0; 2884 foreach($row as $col) { 2885 $worksheet[0]->write($rowno, $colno, $col); 2886 $colno++; 2887 } 2888 $rowno++; 2889 } 2890 $workbook->close(); 2891 return $filename; 2892 } 2893 2894 /** 2895 * @global object 2896 * @param array $export 2897 * @param string $dataname 2898 * @param int $count 2899 * @param string 2900 */ 2901 function data_export_ods($export, $dataname, $count) { 2902 global $CFG; 2903 require_once("$CFG->libdir/odslib.class.php"); 2904 $filename = clean_filename("{$dataname}-{$count}_record"); 2905 if ($count > 1) { 2906 $filename .= 's'; 2907 } 2908 $filename .= clean_filename('-' . gmdate("Ymd_Hi")); 2909 $filename .= '.ods'; 2910 $filearg = '-'; 2911 $workbook = new MoodleODSWorkbook($filearg); 2912 $workbook->send($filename); 2913 $worksheet = array(); 2914 $worksheet[0] = $workbook->add_worksheet(''); 2915 $rowno = 0; 2916 foreach ($export as $row) { 2917 $colno = 0; 2918 foreach($row as $col) { 2919 $worksheet[0]->write($rowno, $colno, $col); 2920 $colno++; 2921 } 2922 $rowno++; 2923 } 2924 $workbook->close(); 2925 return $filename; 2926 } 2927 2928 /** 2929 * @global object 2930 * @param int $dataid 2931 * @param array $fields 2932 * @param array $selectedfields 2933 * @param int $currentgroup group ID of the current group. This is used for 2934 * exporting data while maintaining group divisions. 2935 * @param object $context the context in which the operation is performed (for capability checks) 2936 * @param bool $userdetails whether to include the details of the record author 2937 * @param bool $time whether to include time created/modified 2938 * @param bool $approval whether to include approval status 2939 * @return array 2940 */ 2941 function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null, 2942 $userdetails=false, $time=false, $approval=false) { 2943 global $DB; 2944 2945 if (is_null($context)) { 2946 $context = context_system::instance(); 2947 } 2948 // exporting user data needs special permission 2949 $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context); 2950 2951 $exportdata = array(); 2952 2953 // populate the header in first row of export 2954 foreach($fields as $key => $field) { 2955 if (!in_array($field->field->id, $selectedfields)) { 2956 // ignore values we aren't exporting 2957 unset($fields[$key]); 2958 } else { 2959 $exportdata[0][] = $field->field->name; 2960 } 2961 } 2962 if ($userdetails) { 2963 $exportdata[0][] = get_string('user'); 2964 $exportdata[0][] = get_string('username'); 2965 $exportdata[0][] = get_string('email'); 2966 } 2967 if ($time) { 2968 $exportdata[0][] = get_string('timeadded', 'data'); 2969 $exportdata[0][] = get_string('timemodified', 'data'); 2970 } 2971 if ($approval) { 2972 $exportdata[0][] = get_string('approved', 'data'); 2973 } 2974 2975 $datarecords = $DB->get_records('data_records', array('dataid'=>$dataid)); 2976 ksort($datarecords); 2977 $line = 1; 2978 foreach($datarecords as $record) { 2979 // get content indexed by fieldid 2980 if ($currentgroup) { 2981 $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?'; 2982 $where = array($record->id, $currentgroup); 2983 } else { 2984 $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?'; 2985 $where = array($record->id); 2986 } 2987 2988 if( $content = $DB->get_records_sql($select, $where) ) { 2989 foreach($fields as $field) { 2990 $contents = ''; 2991 if(isset($content[$field->field->id])) { 2992 $contents = $field->export_text_value($content[$field->field->id]); 2993 } 2994 $exportdata[$line][] = $contents; 2995 } 2996 if ($userdetails) { // Add user details to the export data 2997 $userdata = get_complete_user_data('id', $record->userid); 2998 $exportdata[$line][] = fullname($userdata); 2999 $exportdata[$line][] = $userdata->username; 3000 $exportdata[$line][] = $userdata->email; 3001 } 3002 if ($time) { // Add time added / modified 3003 $exportdata[$line][] = userdate($record->timecreated); 3004 $exportdata[$line][] = userdate($record->timemodified); 3005 } 3006 if ($approval) { // Add approval status 3007 $exportdata[$line][] = (int) $record->approved; 3008 } 3009 } 3010 $line++; 3011 } 3012 $line--; 3013 return $exportdata; 3014 } 3015 3016 //////////////////////////////////////////////////////////////////////////////// 3017 // File API // 3018 //////////////////////////////////////////////////////////////////////////////// 3019 3020 /** 3021 * Lists all browsable file areas 3022 * 3023 * @package mod_data 3024 * @category files 3025 * @param stdClass $course course object 3026 * @param stdClass $cm course module object 3027 * @param stdClass $context context object 3028 * @return array 3029 */ 3030 function data_get_file_areas($course, $cm, $context) { 3031 return array('content' => get_string('areacontent', 'mod_data')); 3032 } 3033 3034 /** 3035 * File browsing support for data module. 3036 * 3037 * @param file_browser $browser 3038 * @param array $areas 3039 * @param stdClass $course 3040 * @param cm_info $cm 3041 * @param context $context 3042 * @param string $filearea 3043 * @param int $itemid 3044 * @param string $filepath 3045 * @param string $filename 3046 * @return file_info_stored file_info_stored instance or null if not found 3047 */ 3048 function data_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { 3049 global $CFG, $DB, $USER; 3050 3051 if ($context->contextlevel != CONTEXT_MODULE) { 3052 return null; 3053 } 3054 3055 if (!isset($areas[$filearea])) { 3056 return null; 3057 } 3058 3059 if (is_null($itemid)) { 3060 require_once($CFG->dirroot.'/mod/data/locallib.php'); 3061 return new data_file_info_container($browser, $course, $cm, $context, $areas, $filearea); 3062 } 3063 3064 if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) { 3065 return null; 3066 } 3067 3068 if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) { 3069 return null; 3070 } 3071 3072 if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) { 3073 return null; 3074 } 3075 3076 if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) { 3077 return null; 3078 } 3079 3080 //check if approved 3081 if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) { 3082 return null; 3083 } 3084 3085 // group access 3086 if ($record->groupid) { 3087 $groupmode = groups_get_activity_groupmode($cm, $course); 3088 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 3089 if (!groups_is_member($record->groupid)) { 3090 return null; 3091 } 3092 } 3093 } 3094 3095 $fieldobj = data_get_field($field, $data, $cm); 3096 3097 $filepath = is_null($filepath) ? '/' : $filepath; 3098 $filename = is_null($filename) ? '.' : $filename; 3099 if (!$fieldobj->file_ok($filepath.$filename)) { 3100 return null; 3101 } 3102 3103 $fs = get_file_storage(); 3104 if (!($storedfile = $fs->get_file($context->id, 'mod_data', $filearea, $itemid, $filepath, $filename))) { 3105 return null; 3106 } 3107 3108 // Checks to see if the user can manage files or is the owner. 3109 // TODO MDL-33805 - Do not use userid here and move the capability check above. 3110 if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) { 3111 return null; 3112 } 3113 3114 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 3115 3116 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); 3117 } 3118 3119 /** 3120 * Serves the data attachments. Implements needed access control ;-) 3121 * 3122 * @package mod_data 3123 * @category files 3124 * @param stdClass $course course object 3125 * @param stdClass $cm course module object 3126 * @param stdClass $context context object 3127 * @param string $filearea file area 3128 * @param array $args extra arguments 3129 * @param bool $forcedownload whether or not force download 3130 * @param array $options additional options affecting the file serving 3131 * @return bool false if file not found, does not return if found - justsend the file 3132 */ 3133 function data_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) { 3134 global $CFG, $DB; 3135 3136 if ($context->contextlevel != CONTEXT_MODULE) { 3137 return false; 3138 } 3139 3140 require_course_login($course, true, $cm); 3141 3142 if ($filearea === 'content') { 3143 $contentid = (int)array_shift($args); 3144 3145 if (!$content = $DB->get_record('data_content', array('id'=>$contentid))) { 3146 return false; 3147 } 3148 3149 if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) { 3150 return false; 3151 } 3152 3153 if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) { 3154 return false; 3155 } 3156 3157 if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) { 3158 return false; 3159 } 3160 3161 if ($data->id != $cm->instance) { 3162 // hacker attempt - context does not match the contentid 3163 return false; 3164 } 3165 3166 //check if approved 3167 if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) { 3168 return false; 3169 } 3170 3171 // group access 3172 if ($record->groupid) { 3173 $groupmode = groups_get_activity_groupmode($cm, $course); 3174 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 3175 if (!groups_is_member($record->groupid)) { 3176 return false; 3177 } 3178 } 3179 } 3180 3181 $fieldobj = data_get_field($field, $data, $cm); 3182 3183 $relativepath = implode('/', $args); 3184 $fullpath = "/$context->id/mod_data/content/$content->id/$relativepath"; 3185 3186 if (!$fieldobj->file_ok($relativepath)) { 3187 return false; 3188 } 3189 3190 $fs = get_file_storage(); 3191 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 3192 return false; 3193 } 3194 3195 // finally send the file 3196 send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security! 3197 } 3198 3199 return false; 3200 } 3201 3202 3203 function data_extend_navigation($navigation, $course, $module, $cm) { 3204 global $CFG, $OUTPUT, $USER, $DB; 3205 3206 $rid = optional_param('rid', 0, PARAM_INT); 3207 3208 $data = $DB->get_record('data', array('id'=>$cm->instance)); 3209 $currentgroup = groups_get_activity_group($cm); 3210 $groupmode = groups_get_activity_groupmode($cm); 3211 3212 $numentries = data_numentries($data); 3213 /// Check the number of entries required against the number of entries already made (doesn't apply to teachers) 3214 if ($data->requiredentries > 0 && $numentries < $data->requiredentries && !has_capability('mod/data:manageentries', context_module::instance($cm->id))) { 3215 $data->entriesleft = $data->requiredentries - $numentries; 3216 $entriesnode = $navigation->add(get_string('entrieslefttoadd', 'data', $data)); 3217 $entriesnode->add_class('note'); 3218 } 3219 3220 $navigation->add(get_string('list', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance))); 3221 if (!empty($rid)) { 3222 $navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'rid'=>$rid))); 3223 } else { 3224 $navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'single'))); 3225 } 3226 $navigation->add(get_string('search', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'asearch'))); 3227 } 3228 3229 /** 3230 * Adds module specific settings to the settings block 3231 * 3232 * @param settings_navigation $settings The settings navigation object 3233 * @param navigation_node $datanode The node to add module settings to 3234 */ 3235 function data_extend_settings_navigation(settings_navigation $settings, navigation_node $datanode) { 3236 global $PAGE, $DB, $CFG, $USER; 3237 3238 $data = $DB->get_record('data', array("id" => $PAGE->cm->instance)); 3239 3240 $currentgroup = groups_get_activity_group($PAGE->cm); 3241 $groupmode = groups_get_activity_groupmode($PAGE->cm); 3242 3243 if (data_user_can_add_entry($data, $currentgroup, $groupmode, $PAGE->cm->context)) { // took out participation list here! 3244 if (empty($editentry)) { //TODO: undefined 3245 $addstring = get_string('add', 'data'); 3246 } else { 3247 $addstring = get_string('editentry', 'data'); 3248 } 3249 $datanode->add($addstring, new moodle_url('/mod/data/edit.php', array('d'=>$PAGE->cm->instance))); 3250 } 3251 3252 if (has_capability(DATA_CAP_EXPORT, $PAGE->cm->context)) { 3253 // The capability required to Export database records is centrally defined in 'lib.php' 3254 // and should be weaker than those required to edit Templates, Fields and Presets. 3255 $datanode->add(get_string('exportentries', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id))); 3256 } 3257 if (has_capability('mod/data:manageentries', $PAGE->cm->context)) { 3258 $datanode->add(get_string('importentries', 'data'), new moodle_url('/mod/data/import.php', array('d'=>$data->id))); 3259 } 3260 3261 if (has_capability('mod/data:managetemplates', $PAGE->cm->context)) { 3262 $currenttab = ''; 3263 if ($currenttab == 'list') { 3264 $defaultemplate = 'listtemplate'; 3265 } else if ($currenttab == 'add') { 3266 $defaultemplate = 'addtemplate'; 3267 } else if ($currenttab == 'asearch') { 3268 $defaultemplate = 'asearchtemplate'; 3269 } else { 3270 $defaultemplate = 'singletemplate'; 3271 } 3272 3273 $templates = $datanode->add(get_string('templates', 'data')); 3274 3275 $templatelist = array ('listtemplate', 'singletemplate', 'asearchtemplate', 'addtemplate', 'rsstemplate', 'csstemplate', 'jstemplate'); 3276 foreach ($templatelist as $template) { 3277 $templates->add(get_string($template, 'data'), new moodle_url('/mod/data/templates.php', array('d'=>$data->id,'mode'=>$template))); 3278 } 3279 3280 $datanode->add(get_string('fields', 'data'), new moodle_url('/mod/data/field.php', array('d'=>$data->id))); 3281 $datanode->add(get_string('presets', 'data'), new moodle_url('/mod/data/preset.php', array('d'=>$data->id))); 3282 } 3283 3284 if (!empty($CFG->enablerssfeeds) && !empty($CFG->data_enablerssfeeds) && $data->rssarticles > 0) { 3285 require_once("$CFG->libdir/rsslib.php"); 3286 3287 $string = get_string('rsstype','forum'); 3288 3289 $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $USER->id, 'mod_data', $data->id)); 3290 $datanode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', '')); 3291 } 3292 } 3293 3294 /** 3295 * Save the database configuration as a preset. 3296 * 3297 * @param stdClass $course The course the database module belongs to. 3298 * @param stdClass $cm The course module record 3299 * @param stdClass $data The database record 3300 * @param string $path 3301 * @return bool 3302 */ 3303 function data_presets_save($course, $cm, $data, $path) { 3304 global $USER; 3305 $fs = get_file_storage(); 3306 $filerecord = new stdClass; 3307 $filerecord->contextid = DATA_PRESET_CONTEXT; 3308 $filerecord->component = DATA_PRESET_COMPONENT; 3309 $filerecord->filearea = DATA_PRESET_FILEAREA; 3310 $filerecord->itemid = 0; 3311 $filerecord->filepath = '/'.$path.'/'; 3312 $filerecord->userid = $USER->id; 3313 3314 $filerecord->filename = 'preset.xml'; 3315 $fs->create_file_from_string($filerecord, data_presets_generate_xml($course, $cm, $data)); 3316 3317 $filerecord->filename = 'singletemplate.html'; 3318 $fs->create_file_from_string($filerecord, $data->singletemplate); 3319 3320 $filerecord->filename = 'listtemplateheader.html'; 3321 $fs->create_file_from_string($filerecord, $data->listtemplateheader); 3322 3323 $filerecord->filename = 'listtemplate.html'; 3324 $fs->create_file_from_string($filerecord, $data->listtemplate); 3325 3326 $filerecord->filename = 'listtemplatefooter.html'; 3327 $fs->create_file_from_string($filerecord, $data->listtemplatefooter); 3328 3329 $filerecord->filename = 'addtemplate.html'; 3330 $fs->create_file_from_string($filerecord, $data->addtemplate); 3331 3332 $filerecord->filename = 'rsstemplate.html'; 3333 $fs->create_file_from_string($filerecord, $data->rsstemplate); 3334 3335 $filerecord->filename = 'rsstitletemplate.html'; 3336 $fs->create_file_from_string($filerecord, $data->rsstitletemplate); 3337 3338 $filerecord->filename = 'csstemplate.css'; 3339 $fs->create_file_from_string($filerecord, $data->csstemplate); 3340 3341 $filerecord->filename = 'jstemplate.js'; 3342 $fs->create_file_from_string($filerecord, $data->jstemplate); 3343 3344 $filerecord->filename = 'asearchtemplate.html'; 3345 $fs->create_file_from_string($filerecord, $data->asearchtemplate); 3346 3347 return true; 3348 } 3349 3350 /** 3351 * Generates the XML for the database module provided 3352 * 3353 * @global moodle_database $DB 3354 * @param stdClass $course The course the database module belongs to. 3355 * @param stdClass $cm The course module record 3356 * @param stdClass $data The database record 3357 * @return string The XML for the preset 3358 */ 3359 function data_presets_generate_xml($course, $cm, $data) { 3360 global $DB; 3361 3362 // Assemble "preset.xml": 3363 $presetxmldata = "<preset>\n\n"; 3364 3365 // Raw settings are not preprocessed during saving of presets 3366 $raw_settings = array( 3367 'intro', 3368 'comments', 3369 'requiredentries', 3370 'requiredentriestoview', 3371 'maxentries', 3372 'rssarticles', 3373 'approval', 3374 'manageapproved', 3375 'defaultsortdir' 3376 ); 3377 3378 $presetxmldata .= "<settings>\n"; 3379 // First, settings that do not require any conversion 3380 foreach ($raw_settings as $setting) { 3381 $presetxmldata .= "<$setting>" . htmlspecialchars($data->$setting) . "</$setting>\n"; 3382 } 3383 3384 // Now specific settings 3385 if ($data->defaultsort > 0 && $sortfield = data_get_field_from_id($data->defaultsort, $data)) { 3386 $presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name) . "</defaultsort>\n"; 3387 } else { 3388 $presetxmldata .= "<defaultsort>0</defaultsort>\n"; 3389 } 3390 $presetxmldata .= "</settings>\n\n"; 3391 // Now for the fields. Grab all that are non-empty 3392 $fields = $DB->get_records('data_fields', array('dataid'=>$data->id)); 3393 ksort($fields); 3394 if (!empty($fields)) { 3395 foreach ($fields as $field) { 3396 $presetxmldata .= "<field>\n"; 3397 foreach ($field as $key => $value) { 3398 if ($value != '' && $key != 'id' && $key != 'dataid') { 3399 $presetxmldata .= "<$key>" . htmlspecialchars($value) . "</$key>\n"; 3400 } 3401 } 3402 $presetxmldata .= "</field>\n\n"; 3403 } 3404 } 3405 $presetxmldata .= '</preset>'; 3406 return $presetxmldata; 3407 } 3408 3409 function data_presets_export($course, $cm, $data, $tostorage=false) { 3410 global $CFG, $DB; 3411 3412 $presetname = clean_filename($data->name) . '-preset-' . gmdate("Ymd_Hi"); 3413 $exportsubdir = "mod_data/presetexport/$presetname"; 3414 make_temp_directory($exportsubdir); 3415 $exportdir = "$CFG->tempdir/$exportsubdir"; 3416 3417 // Assemble "preset.xml": 3418 $presetxmldata = data_presets_generate_xml($course, $cm, $data); 3419 3420 // After opening a file in write mode, close it asap 3421 $presetxmlfile = fopen($exportdir . '/preset.xml', 'w'); 3422 fwrite($presetxmlfile, $presetxmldata); 3423 fclose($presetxmlfile); 3424 3425 // Now write the template files 3426 $singletemplate = fopen($exportdir . '/singletemplate.html', 'w'); 3427 fwrite($singletemplate, $data->singletemplate); 3428 fclose($singletemplate); 3429 3430 $listtemplateheader = fopen($exportdir . '/listtemplateheader.html', 'w'); 3431 fwrite($listtemplateheader, $data->listtemplateheader); 3432 fclose($listtemplateheader); 3433 3434 $listtemplate = fopen($exportdir . '/listtemplate.html', 'w'); 3435 fwrite($listtemplate, $data->listtemplate); 3436 fclose($listtemplate); 3437 3438 $listtemplatefooter = fopen($exportdir . '/listtemplatefooter.html', 'w'); 3439 fwrite($listtemplatefooter, $data->listtemplatefooter); 3440 fclose($listtemplatefooter); 3441 3442 $addtemplate = fopen($exportdir . '/addtemplate.html', 'w'); 3443 fwrite($addtemplate, $data->addtemplate); 3444 fclose($addtemplate); 3445 3446 $rsstemplate = fopen($exportdir . '/rsstemplate.html', 'w'); 3447 fwrite($rsstemplate, $data->rsstemplate); 3448 fclose($rsstemplate); 3449 3450 $rsstitletemplate = fopen($exportdir . '/rsstitletemplate.html', 'w'); 3451 fwrite($rsstitletemplate, $data->rsstitletemplate); 3452 fclose($rsstitletemplate); 3453 3454 $csstemplate = fopen($exportdir . '/csstemplate.css', 'w'); 3455 fwrite($csstemplate, $data->csstemplate); 3456 fclose($csstemplate); 3457 3458 $jstemplate = fopen($exportdir . '/jstemplate.js', 'w'); 3459 fwrite($jstemplate, $data->jstemplate); 3460 fclose($jstemplate); 3461 3462 $asearchtemplate = fopen($exportdir . '/asearchtemplate.html', 'w'); 3463 fwrite($asearchtemplate, $data->asearchtemplate); 3464 fclose($asearchtemplate); 3465 3466 // Check if all files have been generated 3467 if (! is_directory_a_preset($exportdir)) { 3468 print_error('generateerror', 'data'); 3469 } 3470 3471 $filenames = array( 3472 'preset.xml', 3473 'singletemplate.html', 3474 'listtemplateheader.html', 3475 'listtemplate.html', 3476 'listtemplatefooter.html', 3477 'addtemplate.html', 3478 'rsstemplate.html', 3479 'rsstitletemplate.html', 3480 'csstemplate.css', 3481 'jstemplate.js', 3482 'asearchtemplate.html' 3483 ); 3484 3485 $filelist = array(); 3486 foreach ($filenames as $filename) { 3487 $filelist[$filename] = $exportdir . '/' . $filename; 3488 } 3489 3490 $exportfile = $exportdir.'.zip'; 3491 file_exists($exportfile) && unlink($exportfile); 3492 3493 $fp = get_file_packer('application/zip'); 3494 $fp->archive_to_pathname($filelist, $exportfile); 3495 3496 foreach ($filelist as $file) { 3497 unlink($file); 3498 } 3499 rmdir($exportdir); 3500 3501 // Return the full path to the exported preset file: 3502 return $exportfile; 3503 } 3504 3505 /** 3506 * Running addtional permission check on plugin, for example, plugins 3507 * may have switch to turn on/off comments option, this callback will 3508 * affect UI display, not like pluginname_comment_validate only throw 3509 * exceptions. 3510 * Capability check has been done in comment->check_permissions(), we 3511 * don't need to do it again here. 3512 * 3513 * @package mod_data 3514 * @category comment 3515 * 3516 * @param stdClass $comment_param { 3517 * context => context the context object 3518 * courseid => int course id 3519 * cm => stdClass course module object 3520 * commentarea => string comment area 3521 * itemid => int itemid 3522 * } 3523 * @return array 3524 */ 3525 function data_comment_permissions($comment_param) { 3526 global $CFG, $DB; 3527 if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) { 3528 throw new comment_exception('invalidcommentitemid'); 3529 } 3530 if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) { 3531 throw new comment_exception('invalidid', 'data'); 3532 } 3533 if ($data->comments) { 3534 return array('post'=>true, 'view'=>true); 3535 } else { 3536 return array('post'=>false, 'view'=>false); 3537 } 3538 } 3539 3540 /** 3541 * Validate comment parameter before perform other comments actions 3542 * 3543 * @package mod_data 3544 * @category comment 3545 * 3546 * @param stdClass $comment_param { 3547 * context => context the context object 3548 * courseid => int course id 3549 * cm => stdClass course module object 3550 * commentarea => string comment area 3551 * itemid => int itemid 3552 * } 3553 * @return boolean 3554 */ 3555 function data_comment_validate($comment_param) { 3556 global $DB; 3557 // validate comment area 3558 if ($comment_param->commentarea != 'database_entry') { 3559 throw new comment_exception('invalidcommentarea'); 3560 } 3561 // validate itemid 3562 if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) { 3563 throw new comment_exception('invalidcommentitemid'); 3564 } 3565 if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) { 3566 throw new comment_exception('invalidid', 'data'); 3567 } 3568 if (!$course = $DB->get_record('course', array('id'=>$data->course))) { 3569 throw new comment_exception('coursemisconf'); 3570 } 3571 if (!$cm = get_coursemodule_from_instance('data', $data->id, $course->id)) { 3572 throw new comment_exception('invalidcoursemodule'); 3573 } 3574 if (!$data->comments) { 3575 throw new comment_exception('commentsoff', 'data'); 3576 } 3577 $context = context_module::instance($cm->id); 3578 3579 //check if approved 3580 if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) { 3581 throw new comment_exception('notapproved', 'data'); 3582 } 3583 3584 // group access 3585 if ($record->groupid) { 3586 $groupmode = groups_get_activity_groupmode($cm, $course); 3587 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 3588 if (!groups_is_member($record->groupid)) { 3589 throw new comment_exception('notmemberofgroup'); 3590 } 3591 } 3592 } 3593 // validate context id 3594 if ($context->id != $comment_param->context->id) { 3595 throw new comment_exception('invalidcontext'); 3596 } 3597 // validation for comment deletion 3598 if (!empty($comment_param->commentid)) { 3599 if ($comment = $DB->get_record('comments', array('id'=>$comment_param->commentid))) { 3600 if ($comment->commentarea != 'database_entry') { 3601 throw new comment_exception('invalidcommentarea'); 3602 } 3603 if ($comment->contextid != $comment_param->context->id) { 3604 throw new comment_exception('invalidcontext'); 3605 } 3606 if ($comment->itemid != $comment_param->itemid) { 3607 throw new comment_exception('invalidcommentitemid'); 3608 } 3609 } else { 3610 throw new comment_exception('invalidcommentid'); 3611 } 3612 } 3613 return true; 3614 } 3615 3616 /** 3617 * Return a list of page types 3618 * @param string $pagetype current page type 3619 * @param stdClass $parentcontext Block's parent context 3620 * @param stdClass $currentcontext Current context of block 3621 */ 3622 function data_page_type_list($pagetype, $parentcontext, $currentcontext) { 3623 $module_pagetype = array('mod-data-*'=>get_string('page-mod-data-x', 'data')); 3624 return $module_pagetype; 3625 } 3626 3627 /** 3628 * Get all of the record ids from a database activity. 3629 * 3630 * @param int $dataid The dataid of the database module. 3631 * @param object $selectdata Contains an additional sql statement for the 3632 * where clause for group and approval fields. 3633 * @param array $params Parameters that coincide with the sql statement. 3634 * @return array $idarray An array of record ids 3635 */ 3636 function data_get_all_recordids($dataid, $selectdata = '', $params = null) { 3637 global $DB; 3638 $initsql = 'SELECT r.id 3639 FROM {data_records} r 3640 WHERE r.dataid = :dataid'; 3641 if ($selectdata != '') { 3642 $initsql .= $selectdata; 3643 $params = array_merge(array('dataid' => $dataid), $params); 3644 } else { 3645 $params = array('dataid' => $dataid); 3646 } 3647 $initsql .= ' GROUP BY r.id'; 3648 $initrecord = $DB->get_recordset_sql($initsql, $params); 3649 $idarray = array(); 3650 foreach ($initrecord as $data) { 3651 $idarray[] = $data->id; 3652 } 3653 // Close the record set and free up resources. 3654 $initrecord->close(); 3655 return $idarray; 3656 } 3657 3658 /** 3659 * Get the ids of all the records that match that advanced search criteria 3660 * This goes and loops through each criterion one at a time until it either 3661 * runs out of records or returns a subset of records. 3662 * 3663 * @param array $recordids An array of record ids. 3664 * @param array $searcharray Contains information for the advanced search criteria 3665 * @param int $dataid The data id of the database. 3666 * @return array $recordids An array of record ids. 3667 */ 3668 function data_get_advance_search_ids($recordids, $searcharray, $dataid) { 3669 // Check to see if we have any record IDs. 3670 if (empty($recordids)) { 3671 // Send back an empty search. 3672 return array(); 3673 } 3674 $searchcriteria = array_keys($searcharray); 3675 // Loop through and reduce the IDs one search criteria at a time. 3676 foreach ($searchcriteria as $key) { 3677 $recordids = data_get_recordids($key, $searcharray, $dataid, $recordids); 3678 // If we don't have anymore IDs then stop. 3679 if (!$recordids) { 3680 break; 3681 } 3682 } 3683 return $recordids; 3684 } 3685 3686 /** 3687 * Gets the record IDs given the search criteria 3688 * 3689 * @param string $alias Record alias. 3690 * @param array $searcharray Criteria for the search. 3691 * @param int $dataid Data ID for the database 3692 * @param array $recordids An array of record IDs. 3693 * @return array $nestarray An arry of record IDs 3694 */ 3695 function data_get_recordids($alias, $searcharray, $dataid, $recordids) { 3696 global $DB; 3697 3698 $nestsearch = $searcharray[$alias]; 3699 // searching for content outside of mdl_data_content 3700 if ($alias < 0) { 3701 $alias = ''; 3702 } 3703 list($insql, $params) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED); 3704 $nestselect = 'SELECT c' . $alias . '.recordid 3705 FROM {data_content} c' . $alias . ', 3706 {data_fields} f, 3707 {data_records} r, 3708 {user} u '; 3709 $nestwhere = 'WHERE u.id = r.userid 3710 AND f.id = c' . $alias . '.fieldid 3711 AND r.id = c' . $alias . '.recordid 3712 AND r.dataid = :dataid 3713 AND c' . $alias .'.recordid ' . $insql . ' 3714 AND '; 3715 3716 $params['dataid'] = $dataid; 3717 if (count($nestsearch->params) != 0) { 3718 $params = array_merge($params, $nestsearch->params); 3719 $nestsql = $nestselect . $nestwhere . $nestsearch->sql; 3720 } else { 3721 $thing = $DB->sql_like($nestsearch->field, ':search1', false); 3722 $nestsql = $nestselect . $nestwhere . $thing . ' GROUP BY c' . $alias . '.recordid'; 3723 $params['search1'] = "%$nestsearch->data%"; 3724 } 3725 $nestrecords = $DB->get_recordset_sql($nestsql, $params); 3726 $nestarray = array(); 3727 foreach ($nestrecords as $data) { 3728 $nestarray[] = $data->recordid; 3729 } 3730 // Close the record set and free up resources. 3731 $nestrecords->close(); 3732 return $nestarray; 3733 } 3734 3735 /** 3736 * Returns an array with an sql string for advanced searches and the parameters that go with them. 3737 * 3738 * @param int $sort DATA_* 3739 * @param stdClass $data Data module object 3740 * @param array $recordids An array of record IDs. 3741 * @param string $selectdata Information for the where and select part of the sql statement. 3742 * @param string $sortorder Additional sort parameters 3743 * @return array sqlselect sqlselect['sql'] has the sql string, sqlselect['params'] contains an array of parameters. 3744 */ 3745 function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $sortorder) { 3746 global $DB; 3747 3748 $namefields = user_picture::fields('u'); 3749 // Remove the id from the string. This already exists in the sql statement. 3750 $namefields = str_replace('u.id,', '', $namefields); 3751 3752 if ($sort == 0) { 3753 $nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ' 3754 FROM {data_content} c, 3755 {data_records} r, 3756 {user} u '; 3757 $groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, ' . $namefields; 3758 } else { 3759 // Sorting through 'Other' criteria 3760 if ($sort <= 0) { 3761 switch ($sort) { 3762 case DATA_LASTNAME: 3763 $sortcontentfull = "u.lastname"; 3764 break; 3765 case DATA_FIRSTNAME: 3766 $sortcontentfull = "u.firstname"; 3767 break; 3768 case DATA_APPROVED: 3769 $sortcontentfull = "r.approved"; 3770 break; 3771 case DATA_TIMEMODIFIED: 3772 $sortcontentfull = "r.timemodified"; 3773 break; 3774 case DATA_TIMEADDED: 3775 default: 3776 $sortcontentfull = "r.timecreated"; 3777 } 3778 } else { 3779 $sortfield = data_get_field_from_id($sort, $data); 3780 $sortcontent = $DB->sql_compare_text('c.' . $sortfield->get_sort_field()); 3781 $sortcontentfull = $sortfield->get_sort_sql($sortcontent); 3782 } 3783 3784 $nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ', 3785 ' . $sortcontentfull . ' 3786 AS sortorder 3787 FROM {data_content} c, 3788 {data_records} r, 3789 {user} u '; 3790 $groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ', ' .$sortcontentfull; 3791 } 3792 3793 // Default to a standard Where statement if $selectdata is empty. 3794 if ($selectdata == '') { 3795 $selectdata = 'WHERE c.recordid = r.id 3796 AND r.dataid = :dataid 3797 AND r.userid = u.id '; 3798 } 3799 3800 // Find the field we are sorting on 3801 if ($sort > 0 or data_get_field_from_id($sort, $data)) { 3802 $selectdata .= ' AND c.fieldid = :sort'; 3803 } 3804 3805 // If there are no record IDs then return an sql statment that will return no rows. 3806 if (count($recordids) != 0) { 3807 list($insql, $inparam) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED); 3808 } else { 3809 list($insql, $inparam) = $DB->get_in_or_equal(array('-1'), SQL_PARAMS_NAMED); 3810 } 3811 $nestfromsql = $selectdata . ' AND c.recordid ' . $insql . $groupsql; 3812 $sqlselect['sql'] = "$nestselectsql $nestfromsql $sortorder"; 3813 $sqlselect['params'] = $inparam; 3814 return $sqlselect; 3815 } 3816 3817 /** 3818 * Checks to see if the user has permission to delete the preset. 3819 * @param stdClass $context Context object. 3820 * @param stdClass $preset The preset object that we are checking for deletion. 3821 * @return bool Returns true if the user can delete, otherwise false. 3822 */ 3823 function data_user_can_delete_preset($context, $preset) { 3824 global $USER; 3825 3826 if (has_capability('mod/data:manageuserpresets', $context)) { 3827 return true; 3828 } else { 3829 $candelete = false; 3830 if ($preset->userid == $USER->id) { 3831 $candelete = true; 3832 } 3833 return $candelete; 3834 } 3835 } 3836 3837 /** 3838 * Delete a record entry. 3839 * 3840 * @param int $recordid The ID for the record to be deleted. 3841 * @param object $data The data object for this activity. 3842 * @param int $courseid ID for the current course (for logging). 3843 * @param int $cmid The course module ID. 3844 * @return bool True if the record deleted, false if not. 3845 */ 3846 function data_delete_record($recordid, $data, $courseid, $cmid) { 3847 global $DB, $CFG; 3848 3849 if ($deleterecord = $DB->get_record('data_records', array('id' => $recordid))) { 3850 if ($deleterecord->dataid == $data->id) { 3851 if ($contents = $DB->get_records('data_content', array('recordid' => $deleterecord->id))) { 3852 foreach ($contents as $content) { 3853 if ($field = data_get_field_from_id($content->fieldid, $data)) { 3854 $field->delete_content($content->recordid); 3855 } 3856 } 3857 $DB->delete_records('data_content', array('recordid'=>$deleterecord->id)); 3858 $DB->delete_records('data_records', array('id'=>$deleterecord->id)); 3859 3860 // Delete cached RSS feeds. 3861 if (!empty($CFG->enablerssfeeds)) { 3862 require_once($CFG->dirroot.'/mod/data/rsslib.php'); 3863 data_rss_delete_file($data); 3864 } 3865 3866 // Trigger an event for deleting this record. 3867 $event = \mod_data\event\record_deleted::create(array( 3868 'objectid' => $deleterecord->id, 3869 'context' => context_module::instance($cmid), 3870 'courseid' => $courseid, 3871 'other' => array( 3872 'dataid' => $deleterecord->dataid 3873 ) 3874 )); 3875 $event->add_record_snapshot('data_records', $deleterecord); 3876 $event->trigger(); 3877 3878 return true; 3879 } 3880 } 3881 } 3882 return false; 3883 } 3884 3885 /** 3886 * Check for required fields, and build a list of fields to be updated in a 3887 * submission. 3888 * 3889 * @param $mod stdClass The current recordid - provided as an optimisation. 3890 * @param $fields array The field data 3891 * @param $datarecord stdClass The submitted data. 3892 * @return stdClass containing: 3893 * * string[] generalnotifications Notifications for the form as a whole. 3894 * * string[] fieldnotifications Notifications for a specific field. 3895 * * bool validated Whether the field was validated successfully. 3896 * * data_field_base[] fields The field objects to be update. 3897 */ 3898 function data_process_submission(stdClass $mod, $fields, stdClass $datarecord) { 3899 $result = new stdClass(); 3900 3901 // Empty form checking - you can't submit an empty form. 3902 $emptyform = true; 3903 $requiredfieldsfilled = true; 3904 $fieldsvalidated = true; 3905 3906 // Store the notifications. 3907 $result->generalnotifications = array(); 3908 $result->fieldnotifications = array(); 3909 3910 // Store the instantiated classes as an optimisation when processing the result. 3911 // This prevents the fields being re-initialised when updating. 3912 $result->fields = array(); 3913 3914 $submitteddata = array(); 3915 foreach ($datarecord as $fieldname => $fieldvalue) { 3916 if (strpos($fieldname, '_')) { 3917 $namearray = explode('_', $fieldname, 3); 3918 $fieldid = $namearray[1]; 3919 if (!isset($submitteddata[$fieldid])) { 3920 $submitteddata[$fieldid] = array(); 3921 } 3922 if (count($namearray) === 2) { 3923 $subfieldid = 0; 3924 } else { 3925 $subfieldid = $namearray[2]; 3926 } 3927 3928 $fielddata = new stdClass(); 3929 $fielddata->fieldname = $fieldname; 3930 $fielddata->value = $fieldvalue; 3931 $submitteddata[$fieldid][$subfieldid] = $fielddata; 3932 } 3933 } 3934 3935 // Check all form fields which have the required are filled. 3936 foreach ($fields as $fieldrecord) { 3937 // Check whether the field has any data. 3938 $fieldhascontent = false; 3939 3940 $field = data_get_field($fieldrecord, $mod); 3941 if (isset($submitteddata[$fieldrecord->id])) { 3942 // Field validation check. 3943 if (method_exists($field, 'field_validation')) { 3944 $errormessage = $field->field_validation($submitteddata[$fieldrecord->id]); 3945 if ($errormessage) { 3946 $result->fieldnotifications[$field->field->name][] = $errormessage; 3947 $fieldsvalidated = false; 3948 } 3949 } 3950 foreach ($submitteddata[$fieldrecord->id] as $fieldname => $value) { 3951 if ($field->notemptyfield($value->value, $value->fieldname)) { 3952 // The field has content and the form is not empty. 3953 $fieldhascontent = true; 3954 $emptyform = false; 3955 } 3956 } 3957 } 3958 3959 // If the field is required, add a notification to that effect. 3960 if ($field->field->required && !$fieldhascontent) { 3961 if (!isset($result->fieldnotifications[$field->field->name])) { 3962 $result->fieldnotifications[$field->field->name] = array(); 3963 } 3964 $result->fieldnotifications[$field->field->name][] = get_string('errormustsupplyvalue', 'data'); 3965 $requiredfieldsfilled = false; 3966 } 3967 3968 // Update the field. 3969 if (isset($submitteddata[$fieldrecord->id])) { 3970 foreach ($submitteddata[$fieldrecord->id] as $value) { 3971 $result->fields[$value->fieldname] = $field; 3972 } 3973 } 3974 } 3975 3976 if ($emptyform) { 3977 // The form is empty. 3978 $result->generalnotifications[] = get_string('emptyaddform', 'data'); 3979 } 3980 3981 $result->validated = $requiredfieldsfilled && !$emptyform && $fieldsvalidated; 3982 3983 return $result; 3984 } 3985 3986 /** 3987 * This standard function will check all instances of this module 3988 * and make sure there are up-to-date events created for each of them. 3989 * If courseid = 0, then every data event in the site is checked, else 3990 * only data events belonging to the course specified are checked. 3991 * This function is used, in its new format, by restore_refresh_events() 3992 * 3993 * @param int $courseid 3994 * @return bool 3995 */ 3996 function data_refresh_events($courseid = 0) { 3997 global $DB, $CFG; 3998 require_once($CFG->dirroot.'/mod/data/locallib.php'); 3999 4000 if ($courseid) { 4001 if (! $data = $DB->get_records("data", array("course" => $courseid))) { 4002 return true; 4003 } 4004 } else { 4005 if (! $data = $DB->get_records("data")) { 4006 return true; 4007 } 4008 } 4009 4010 foreach ($data as $datum) { 4011 data_set_events($datum); 4012 } 4013 return true; 4014 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |