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