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