[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * @package mod_data 20 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 require_once($CFG->dirroot . '/mod/data/lib.php'); 27 require_once($CFG->libdir . '/portfolio/caller.php'); 28 require_once($CFG->libdir . '/filelib.php'); 29 30 /** 31 * The class to handle entry exports of a database module 32 */ 33 class data_portfolio_caller extends portfolio_module_caller_base { 34 35 /** @var int the single record to export */ 36 protected $recordid; 37 38 /** @var object the record from the data table */ 39 private $data; 40 41 /**#@+ @var array the fields used and their fieldtypes */ 42 private $fields; 43 private $fieldtypes; 44 45 /** @var object the records to export */ 46 private $records; 47 48 /** @var int how many records are 'mine' */ 49 private $minecount; 50 51 /** 52 * the required callback arguments for a single-record export 53 * 54 * @return array 55 */ 56 public static function expected_callbackargs() { 57 return array( 58 'id' => true, 59 'recordid' => false, 60 ); 61 } 62 63 /** 64 * @param array $callbackargs the arguments passed through 65 */ 66 public function __construct($callbackargs) { 67 parent::__construct($callbackargs); 68 // set up the list of fields to export 69 $this->selectedfields = array(); 70 foreach ($callbackargs as $key => $value) { 71 if (strpos($key, 'field_') === 0) { 72 $this->selectedfields[] = substr($key, 6); 73 } 74 } 75 } 76 77 /** 78 * load up the data needed for the export 79 * 80 * @global object $DB 81 */ 82 public function load_data() { 83 global $DB, $USER; 84 if (!$this->cm = get_coursemodule_from_id('data', $this->id)) { 85 throw new portfolio_caller_exception('invalidid', 'data'); 86 } 87 if (!$this->data = $DB->get_record('data', array('id' => $this->cm->instance))) { 88 throw new portfolio_caller_exception('invalidid', 'data'); 89 } 90 $fieldrecords = $DB->get_records('data_fields', array('dataid' => $this->cm->instance), 'id'); 91 // populate objets for this databases fields 92 $this->fields = array(); 93 foreach ($fieldrecords as $fieldrecord) { 94 $tmp = data_get_field($fieldrecord, $this->data); 95 $this->fields[] = $tmp; 96 $this->fieldtypes[] = $tmp->type; 97 } 98 99 $this->records = array(); 100 if ($this->recordid) { 101 $tmp = $DB->get_record('data_records', array('id' => $this->recordid)); 102 $tmp->content = $DB->get_records('data_content', array('recordid' => $this->recordid)); 103 $this->records[] = $tmp; 104 } else { 105 $where = array('dataid' => $this->data->id); 106 if (!has_capability('mod/data:exportallentries', context_module::instance($this->cm->id))) { 107 $where['userid'] = $USER->id; // get them all in case, we'll unset ones that aren't ours later if necessary 108 } 109 $tmp = $DB->get_records('data_records', $where); 110 foreach ($tmp as $t) { 111 $t->content = $DB->get_records('data_content', array('recordid' => $t->id)); 112 $this->records[] = $t; 113 } 114 $this->minecount = $DB->count_records('data_records', array('dataid' => $this->data->id, 'userid' => $USER->id)); 115 } 116 117 if ($this->recordid) { 118 list($formats, $files) = self::formats($this->fields, $this->records[0]); 119 $this->set_file_and_format_data($files); 120 } 121 } 122 123 /** 124 * How long we think the export will take 125 * Single entry is probably not too long. 126 * But we check for filesizes 127 * Else base it on the number of records 128 * 129 * @return one of PORTFOLIO_TIME_XX constants 130 */ 131 public function expected_time() { 132 if ($this->recordid) { 133 return $this->expected_time_file(); 134 } else { 135 return portfolio_expected_time_db(count($this->records)); 136 } 137 } 138 139 /** 140 * Calculate the shal1 of this export 141 * Dependent on the export format. 142 * @return string 143 */ 144 public function get_sha1() { 145 // in the case that we're exporting a subclass of 'file' and we have a singlefile, 146 // then we're not exporting any metadata, just the file by itself by mimetype. 147 if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) { 148 return $this->get_sha1_file(); 149 } 150 // otherwise we're exporting some sort of multipart content so use the data 151 $str = ''; 152 foreach ($this->records as $record) { 153 foreach ($record as $data) { 154 if (is_array($data) || is_object($data)) { 155 $keys = array_keys($data); 156 $testkey = array_pop($keys); 157 if (is_array($data[$testkey]) || is_object($data[$testkey])) { 158 foreach ($data as $d) { 159 $str .= implode(',', (array)$d); 160 } 161 } else { 162 $str .= implode(',', (array)$data); 163 } 164 } else { 165 $str .= $data; 166 } 167 } 168 } 169 return sha1($str . ',' . $this->exporter->get('formatclass')); 170 } 171 172 /** 173 * Prepare the package for export 174 * 175 * @return stored_file object 176 */ 177 public function prepare_package() { 178 global $DB; 179 $leapwriter = null; 180 $content = ''; 181 $filename = ''; 182 $uid = $this->exporter->get('user')->id; 183 $users = array(); //cache 184 $onlymine = $this->get_export_config('mineonly'); 185 if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) { 186 $leapwriter = $this->exporter->get('format')->leap2a_writer(); 187 $ids = array(); 188 } 189 190 if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) { 191 return $this->get('exporter')->copy_existing_file($this->singlefile); 192 } 193 foreach ($this->records as $key => $record) { 194 if ($onlymine && $record->userid != $uid) { 195 unset($this->records[$key]); // sha1 196 continue; 197 } 198 list($tmpcontent, $files) = $this->exportentry($record); 199 $content .= $tmpcontent; 200 if ($leapwriter) { 201 $entry = new portfolio_format_leap2a_entry('dataentry' . $record->id, $this->data->name, 'resource', $tmpcontent); 202 $entry->published = $record->timecreated; 203 $entry->updated = $record->timemodified; 204 if ($record->userid != $uid) { 205 if (!array_key_exists($record->userid, $users)) { 206 $users[$record->userid] = $DB->get_record('user', array('id' => $record->userid), 'id,firstname,lastname'); 207 } 208 $entry->author = $users[$record->userid]; 209 } 210 $ids[] = $entry->id; 211 $leapwriter->link_files($entry, $files, 'dataentry' . $record->id . 'file'); 212 $leapwriter->add_entry($entry); 213 } 214 } 215 if ($leapwriter) { 216 if (count($this->records) > 1) { // make a selection element to tie them all together 217 $selection = new portfolio_format_leap2a_entry('datadb' . $this->data->id, 218 get_string('entries', 'data') . ': ' . $this->data->name, 'selection'); 219 $leapwriter->add_entry($selection); 220 $leapwriter->make_selection($selection, $ids, 'Grouping'); 221 } 222 $filename = $this->exporter->get('format')->manifest_name(); 223 $content = $leapwriter->to_xml(); 224 } else { 225 if (count($this->records) == 1) { 226 $filename = clean_filename($this->cm->name . '-entry.html'); 227 } else { 228 $filename = clean_filename($this->cm->name . '-full.html'); 229 } 230 } 231 return $this->exporter->write_new_file( 232 $content, 233 $filename, 234 ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH) // if we have associate files, this is a 'manifest' 235 ); 236 } 237 238 /** 239 * Verify the user can still export this entry 240 * 241 * @return bool 242 */ 243 public function check_permissions() { 244 if ($this->recordid) { 245 if (data_isowner($this->recordid)) { 246 return has_capability('mod/data:exportownentry', context_module::instance($this->cm->id)); 247 } 248 return has_capability('mod/data:exportentry', context_module::instance($this->cm->id)); 249 } 250 if ($this->has_export_config() && !$this->get_export_config('mineonly')) { 251 return has_capability('mod/data:exportallentries', context_module::instance($this->cm->id)); 252 } 253 return has_capability('mod/data:exportownentry', context_module::instance($this->cm->id)); 254 } 255 256 /** 257 * @return string 258 */ 259 public static function display_name() { 260 return get_string('modulename', 'data'); 261 } 262 263 /** 264 * @global object 265 * @return bool|void 266 */ 267 public function __wakeup() { 268 global $CFG; 269 if (empty($CFG)) { 270 return true; // too early yet 271 } 272 foreach ($this->fieldtypes as $key => $field) { 273 require_once($CFG->dirroot . '/mod/data/field/' . $field .'/field.class.php'); 274 $this->fields[$key] = unserialize(serialize($this->fields[$key])); 275 } 276 } 277 278 /** 279 * Prepare a single entry for export, replacing all the content etc 280 * 281 * @param stdclass $record the entry to export 282 * 283 * @return array with key 0 = the html content, key 1 = array of attachments 284 */ 285 private function exportentry($record) { 286 // Replacing tags 287 $patterns = array(); 288 $replacement = array(); 289 290 $files = array(); 291 // Then we generate strings to replace for normal tags 292 $format = $this->get('exporter')->get('format'); 293 foreach ($this->fields as $field) { 294 $patterns[]='[['.$field->field->name.']]'; 295 if (is_callable(array($field, 'get_file'))) { 296 if (!$file = $field->get_file($record->id)) { 297 $replacement[] = ''; 298 continue; // probably left empty 299 } 300 $replacement[] = $format->file_output($file); 301 $this->get('exporter')->copy_existing_file($file); 302 $files[] = $file; 303 } else { 304 $replacement[] = $field->display_browse_field($record->id, 'singletemplate'); 305 } 306 } 307 308 // Replacing special tags (##Edit##, ##Delete##, ##More##) 309 $patterns[]='##edit##'; 310 $patterns[]='##delete##'; 311 $patterns[]='##export##'; 312 $patterns[]='##more##'; 313 $patterns[]='##moreurl##'; 314 $patterns[]='##user##'; 315 $patterns[]='##approve##'; 316 $patterns[]='##disapprove##'; 317 $patterns[]='##comments##'; 318 $patterns[] = '##timeadded##'; 319 $patterns[] = '##timemodified##'; 320 $replacement[] = ''; 321 $replacement[] = ''; 322 $replacement[] = ''; 323 $replacement[] = ''; 324 $replacement[] = ''; 325 $replacement[] = ''; 326 $replacement[] = ''; 327 $replacement[] = ''; 328 $replacement[] = ''; 329 $replacement[] = userdate($record->timecreated); 330 $replacement[] = userdate($record->timemodified); 331 332 // actual replacement of the tags 333 return array(str_ireplace($patterns, $replacement, $this->data->singletemplate), $files); 334 } 335 336 /** 337 * Given the fields being exported, and the single record, 338 * work out which export format(s) we can use 339 * 340 * @param array $fields array of field objects 341 * @param object $record The data record object 342 * 343 * @uses PORTFOLIO_FORMAT_PLAINHTML 344 * @uses PORTFOLIO_FORMAT_RICHHTML 345 * 346 * @return array of PORTFOLIO_XX constants 347 */ 348 public static function formats($fields, $record) { 349 $formats = array(PORTFOLIO_FORMAT_PLAINHTML); 350 $includedfiles = array(); 351 foreach ($fields as $singlefield) { 352 if (is_callable(array($singlefield, 'get_file'))) { 353 if ($file = $singlefield->get_file($record->id)) { 354 $includedfiles[] = $file; 355 } 356 } 357 } 358 if (count($includedfiles) == 1 && count($fields) == 1) { 359 $formats = array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype())); 360 } else if (count($includedfiles) > 0) { 361 $formats = array(PORTFOLIO_FORMAT_RICHHTML); 362 } 363 return array($formats, $includedfiles); 364 } 365 366 public static function has_files($data) { 367 global $DB; 368 $fieldrecords = $DB->get_records('data_fields', array('dataid' => $data->id), 'id'); 369 // populate objets for this databases fields 370 foreach ($fieldrecords as $fieldrecord) { 371 $field = data_get_field($fieldrecord, $data); 372 if (is_callable(array($field, 'get_file'))) { 373 return true; 374 } 375 } 376 return false; 377 } 378 379 /** 380 * base supported formats before we know anything about the export 381 */ 382 public static function base_supported_formats() { 383 return array(PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A); 384 } 385 386 public function has_export_config() { 387 // if we're exporting more than just a single entry, 388 // and we have the capability to export all entries, 389 // then ask whether we want just our own, or all of them 390 return (empty($this->recordid) // multi-entry export 391 && $this->minecount > 0 // some of them are mine 392 && $this->minecount != count($this->records) // not all of them are mine 393 && has_capability('mod/data:exportallentries', context_module::instance($this->cm->id))); // they actually have a choice in the matter 394 } 395 396 public function export_config_form(&$mform, $instance) { 397 if (!$this->has_export_config()) { 398 return; 399 } 400 $mform->addElement('selectyesno', 'mineonly', get_string('exportownentries', 'data', (object)array('mine' => $this->minecount, 'all' => count($this->records)))); 401 $mform->setDefault('mineonly', 1); 402 } 403 404 public function get_allowed_export_config() { 405 return array('mineonly'); 406 } 407 } 408 409 410 /** 411 * Class representing the virtual node with all itemids in the file browser 412 * 413 * @category files 414 * @copyright 2012 David Mudrak <david@moodle.com> 415 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 416 */ 417 class data_file_info_container extends file_info { 418 /** @var file_browser */ 419 protected $browser; 420 /** @var stdClass */ 421 protected $course; 422 /** @var stdClass */ 423 protected $cm; 424 /** @var string */ 425 protected $component; 426 /** @var stdClass */ 427 protected $context; 428 /** @var array */ 429 protected $areas; 430 /** @var string */ 431 protected $filearea; 432 433 /** 434 * Constructor (in case you did not realize it ;-) 435 * 436 * @param file_browser $browser 437 * @param stdClass $course 438 * @param stdClass $cm 439 * @param stdClass $context 440 * @param array $areas 441 * @param string $filearea 442 */ 443 public function __construct($browser, $course, $cm, $context, $areas, $filearea) { 444 parent::__construct($browser, $context); 445 $this->browser = $browser; 446 $this->course = $course; 447 $this->cm = $cm; 448 $this->component = 'mod_data'; 449 $this->context = $context; 450 $this->areas = $areas; 451 $this->filearea = $filearea; 452 } 453 454 /** 455 * @return array with keys contextid, filearea, itemid, filepath and filename 456 */ 457 public function get_params() { 458 return array( 459 'contextid' => $this->context->id, 460 'component' => $this->component, 461 'filearea' => $this->filearea, 462 'itemid' => null, 463 'filepath' => null, 464 'filename' => null, 465 ); 466 } 467 468 /** 469 * Can new files or directories be added via the file browser 470 * 471 * @return bool 472 */ 473 public function is_writable() { 474 return false; 475 } 476 477 /** 478 * Should this node be considered as a folder in the file browser 479 * 480 * @return bool 481 */ 482 public function is_directory() { 483 return true; 484 } 485 486 /** 487 * Returns localised visible name of this node 488 * 489 * @return string 490 */ 491 public function get_visible_name() { 492 return $this->areas[$this->filearea]; 493 } 494 495 /** 496 * Returns list of children nodes 497 * 498 * @return array of file_info instances 499 */ 500 public function get_children() { 501 return $this->get_filtered_children('*', false, true); 502 } 503 504 /** 505 * Help function to return files matching extensions or their count 506 * 507 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 508 * @param bool|int $countonly if false returns the children, if an int returns just the 509 * count of children but stops counting when $countonly number of children is reached 510 * @param bool $returnemptyfolders if true returns items that don't have matching files inside 511 * @return array|int array of file_info instances or the count 512 */ 513 private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) { 514 global $DB; 515 $params = array('contextid' => $this->context->id, 516 'component' => $this->component, 517 'filearea' => $this->filearea); 518 $sql = 'SELECT DISTINCT itemid 519 FROM {files} 520 WHERE contextid = :contextid 521 AND component = :component 522 AND filearea = :filearea'; 523 if (!$returnemptyfolders) { 524 $sql .= ' AND filename <> :emptyfilename'; 525 $params['emptyfilename'] = '.'; 526 } 527 list($sql2, $params2) = $this->build_search_files_sql($extensions); 528 $sql .= ' '.$sql2; 529 $params = array_merge($params, $params2); 530 if ($countonly === false) { 531 $sql .= ' ORDER BY itemid DESC'; 532 } 533 534 $rs = $DB->get_recordset_sql($sql, $params); 535 $children = array(); 536 foreach ($rs as $record) { 537 if ($child = $this->browser->get_file_info($this->context, 'mod_data', $this->filearea, $record->itemid)) { 538 $children[] = $child; 539 } 540 if ($countonly !== false && count($children) >= $countonly) { 541 break; 542 } 543 } 544 $rs->close(); 545 if ($countonly !== false) { 546 return count($children); 547 } 548 return $children; 549 } 550 551 /** 552 * Returns list of children which are either files matching the specified extensions 553 * or folders that contain at least one such file. 554 * 555 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 556 * @return array of file_info instances 557 */ 558 public function get_non_empty_children($extensions = '*') { 559 return $this->get_filtered_children($extensions, false); 560 } 561 562 /** 563 * Returns the number of children which are either files matching the specified extensions 564 * or folders containing at least one such file. 565 * 566 * @param string|array $extensions, for example '*' or array('.gif','.jpg') 567 * @param int $limit stop counting after at least $limit non-empty children are found 568 * @return int 569 */ 570 public function count_non_empty_children($extensions = '*', $limit = 1) { 571 return $this->get_filtered_children($extensions, $limit); 572 } 573 574 /** 575 * Returns parent file_info instance 576 * 577 * @return file_info or null for root 578 */ 579 public function get_parent() { 580 return $this->browser->get_file_info($this->context); 581 } 582 } 583 584 /** 585 * This creates new calendar events given as timeavailablefrom and timeclose by $data. 586 * 587 * @param stdClass $data 588 * @return void 589 */ 590 function data_set_events($data) { 591 global $DB, $CFG; 592 593 require_once($CFG->dirroot.'/calendar/lib.php'); 594 595 // Get CMID if not sent as part of $data. 596 if (!isset($data->coursemodule)) { 597 $cm = get_coursemodule_from_instance('data', $data->id, $data->course); 598 $data->coursemodule = $cm->id; 599 } 600 // Data start calendar events. 601 $event = new stdClass(); 602 if ($event->id = $DB->get_field('event', 'id', 603 array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open'))) { 604 if ($data->timeavailablefrom > 0) { 605 // Calendar event exists so update it. 606 $event->name = get_string('calendarstart', 'data', $data->name); 607 $event->description = format_module_intro('data', $data, $data->coursemodule); 608 $event->timestart = $data->timeavailablefrom; 609 $event->visible = instance_is_visible('data', $data); 610 $event->timeduration = 0; 611 $calendarevent = calendar_event::load($event->id); 612 $calendarevent->update($event); 613 } else { 614 // Calendar event is on longer needed. 615 $calendarevent = calendar_event::load($event->id); 616 $calendarevent->delete(); 617 } 618 } else { 619 // Event doesn't exist so create one. 620 if (isset($data->timeavailablefrom) && $data->timeavailablefrom > 0) { 621 $event->name = get_string('calendarstart', 'data', $data->name); 622 $event->description = format_module_intro('data', $data, $data->coursemodule); 623 $event->courseid = $data->course; 624 $event->groupid = 0; 625 $event->userid = 0; 626 $event->modulename = 'data'; 627 $event->instance = $data->id; 628 $event->eventtype = 'open'; 629 $event->timestart = $data->timeavailablefrom; 630 $event->visible = instance_is_visible('data', $data); 631 $event->timeduration = 0; 632 calendar_event::create($event); 633 } 634 } 635 636 // Data end calendar events. 637 $event = new stdClass(); 638 if ($event->id = $DB->get_field('event', 'id', 639 array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close'))) { 640 if ($data->timeavailableto > 0) { 641 // Calendar event exists so update it. 642 $event->name = get_string('calendarend', 'data', $data->name); 643 $event->description = format_module_intro('data', $data, $data->coursemodule); 644 $event->timestart = $data->timeavailableto; 645 $event->visible = instance_is_visible('data', $data); 646 $event->timeduration = 0; 647 $calendarevent = calendar_event::load($event->id); 648 $calendarevent->update($event); 649 } else { 650 // Calendar event is on longer needed. 651 $calendarevent = calendar_event::load($event->id); 652 $calendarevent->delete(); 653 } 654 } else { 655 // Event doesn't exist so create one. 656 if (isset($data->timeavailableto) && $data->timeavailableto > 0) { 657 $event = new stdClass(); 658 $event->name = get_string('calendarend', 'data', $data->name); 659 $event->description = format_module_intro('data', $data, $data->coursemodule); 660 $event->courseid = $data->course; 661 $event->groupid = 0; 662 $event->userid = 0; 663 $event->modulename = 'data'; 664 $event->instance = $data->id; 665 $event->eventtype = 'close'; 666 $event->timestart = $data->timeavailableto; 667 $event->visible = instance_is_visible('data', $data); 668 $event->timeduration = 0; 669 calendar_event::create($event); 670 } 671 } 672 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |