[ 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 * Library of functions for forum outside of the core api 20 */ 21 22 require_once($CFG->dirroot . '/mod/forum/lib.php'); 23 require_once($CFG->libdir . '/portfolio/caller.php'); 24 25 /** 26 * @package mod_forum 27 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 */ 30 class forum_portfolio_caller extends portfolio_module_caller_base { 31 32 protected $postid; 33 protected $discussionid; 34 protected $attachment; 35 36 private $post; 37 private $forum; 38 private $discussion; 39 private $posts; 40 private $keyedfiles; // just using multifiles isn't enough if we're exporting a full thread 41 42 /** 43 * @return array 44 */ 45 public static function expected_callbackargs() { 46 return array( 47 'postid' => false, 48 'discussionid' => false, 49 'attachment' => false, 50 ); 51 } 52 /** 53 * @param array $callbackargs 54 */ 55 function __construct($callbackargs) { 56 parent::__construct($callbackargs); 57 if (!$this->postid && !$this->discussionid) { 58 throw new portfolio_caller_exception('mustprovidediscussionorpost', 'forum'); 59 } 60 } 61 /** 62 * @global object 63 */ 64 public function load_data() { 65 global $DB; 66 67 if ($this->postid) { 68 if (!$this->post = $DB->get_record('forum_posts', array('id' => $this->postid))) { 69 throw new portfolio_caller_exception('invalidpostid', 'forum'); 70 } 71 } 72 73 $dparams = array(); 74 if ($this->discussionid) { 75 $dbparams = array('id' => $this->discussionid); 76 } else if ($this->post) { 77 $dbparams = array('id' => $this->post->discussion); 78 } else { 79 throw new portfolio_caller_exception('mustprovidediscussionorpost', 'forum'); 80 } 81 82 if (!$this->discussion = $DB->get_record('forum_discussions', $dbparams)) { 83 throw new portfolio_caller_exception('invaliddiscussionid', 'forum'); 84 } 85 86 if (!$this->forum = $DB->get_record('forum', array('id' => $this->discussion->forum))) { 87 throw new portfolio_caller_exception('invalidforumid', 'forum'); 88 } 89 90 if (!$this->cm = get_coursemodule_from_instance('forum', $this->forum->id)) { 91 throw new portfolio_caller_exception('invalidcoursemodule'); 92 } 93 94 $this->modcontext = context_module::instance($this->cm->id); 95 $fs = get_file_storage(); 96 if ($this->post) { 97 if ($this->attachment) { 98 $this->set_file_and_format_data($this->attachment); 99 } else { 100 $attach = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'attachment', $this->post->id, 'timemodified', false); 101 $embed = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'post', $this->post->id, 'timemodified', false); 102 $files = array_merge($attach, $embed); 103 $this->set_file_and_format_data($files); 104 } 105 if (!empty($this->multifiles)) { 106 $this->keyedfiles[$this->post->id] = $this->multifiles; 107 } else if (!empty($this->singlefile)) { 108 $this->keyedfiles[$this->post->id] = array($this->singlefile); 109 } 110 } else { // whole thread 111 $fs = get_file_storage(); 112 $this->posts = forum_get_all_discussion_posts($this->discussion->id, 'p.created ASC'); 113 $this->multifiles = array(); 114 foreach ($this->posts as $post) { 115 $attach = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'attachment', $post->id, 'timemodified', false); 116 $embed = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'post', $post->id, 'timemodified', false); 117 $files = array_merge($attach, $embed); 118 if ($files) { 119 $this->keyedfiles[$post->id] = $files; 120 } else { 121 continue; 122 } 123 $this->multifiles = array_merge($this->multifiles, array_values($this->keyedfiles[$post->id])); 124 } 125 } 126 if (empty($this->multifiles) && !empty($this->singlefile)) { 127 $this->multifiles = array($this->singlefile); // copy_files workaround 128 } 129 // depending on whether there are files or not, we might have to change richhtml/plainhtml 130 if (empty($this->attachment)) { 131 if (!empty($this->multifiles)) { 132 $this->add_format(PORTFOLIO_FORMAT_RICHHTML); 133 } else { 134 $this->add_format(PORTFOLIO_FORMAT_PLAINHTML); 135 } 136 } 137 } 138 139 /** 140 * @global object 141 * @return string 142 */ 143 function get_return_url() { 144 global $CFG; 145 return $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $this->discussion->id; 146 } 147 /** 148 * @global object 149 * @return array 150 */ 151 function get_navigation() { 152 global $CFG; 153 154 $navlinks = array(); 155 $navlinks[] = array( 156 'name' => format_string($this->discussion->name), 157 'link' => $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $this->discussion->id, 158 'type' => 'title' 159 ); 160 return array($navlinks, $this->cm); 161 } 162 /** 163 * either a whole discussion 164 * a single post, with or without attachment 165 * or just an attachment with no post 166 * 167 * @global object 168 * @global object 169 * @uses PORTFOLIO_FORMAT_RICH 170 * @return mixed 171 */ 172 function prepare_package() { 173 global $CFG; 174 175 // set up the leap2a writer if we need it 176 $writingleap = false; 177 if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) { 178 $leapwriter = $this->exporter->get('format')->leap2a_writer(); 179 $writingleap = true; 180 } 181 if ($this->attachment) { // simplest case first - single file attachment 182 $this->copy_files(array($this->singlefile), $this->attachment); 183 if ($writingleap) { // if we're writing leap, make the manifest to go along with the file 184 $entry = new portfolio_format_leap2a_file($this->singlefile->get_filename(), $this->singlefile); 185 $leapwriter->add_entry($entry); 186 return $this->exporter->write_new_file($leapwriter->to_xml(), $this->exporter->get('format')->manifest_name(), true); 187 } 188 189 } else if (empty($this->post)) { // exporting whole discussion 190 $content = ''; // if we're just writing HTML, start a string to add each post to 191 $ids = array(); // if we're writing leap2a, keep track of all entryids so we can add a selection element 192 foreach ($this->posts as $post) { 193 $posthtml = $this->prepare_post($post); 194 if ($writingleap) { 195 $ids[] = $this->prepare_post_leap2a($leapwriter, $post, $posthtml); 196 } else { 197 $content .= $posthtml . '<br /><br />'; 198 } 199 } 200 $this->copy_files($this->multifiles); 201 $name = 'discussion.html'; 202 $manifest = ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH); 203 if ($writingleap) { 204 // add on an extra 'selection' entry 205 $selection = new portfolio_format_leap2a_entry('forumdiscussion' . $this->discussionid, 206 get_string('discussion', 'forum') . ': ' . $this->discussion->name, 'selection'); 207 $leapwriter->add_entry($selection); 208 $leapwriter->make_selection($selection, $ids, 'Grouping'); 209 $content = $leapwriter->to_xml(); 210 $name = $this->get('exporter')->get('format')->manifest_name(); 211 } 212 $this->get('exporter')->write_new_file($content, $name, $manifest); 213 214 } else { // exporting a single post 215 $posthtml = $this->prepare_post($this->post); 216 217 $content = $posthtml; 218 $name = 'post.html'; 219 $manifest = ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH); 220 221 if ($writingleap) { 222 $this->prepare_post_leap2a($leapwriter, $this->post, $posthtml); 223 $content = $leapwriter->to_xml(); 224 $name = $this->exporter->get('format')->manifest_name(); 225 } 226 $this->copy_files($this->multifiles); 227 $this->get('exporter')->write_new_file($content, $name, $manifest); 228 } 229 } 230 231 /** 232 * helper function to add a leap2a entry element 233 * that corresponds to a single forum post, 234 * including any attachments 235 * 236 * the entry/ies are added directly to the leapwriter, which is passed by ref 237 * 238 * @param portfolio_format_leap2a_writer $leapwriter writer object to add entries to 239 * @param object $post the stdclass object representing the database record 240 * @param string $posthtml the content of the post (prepared by {@link prepare_post} 241 * 242 * @return int id of new entry 243 */ 244 private function prepare_post_leap2a(portfolio_format_leap2a_writer $leapwriter, $post, $posthtml) { 245 $entry = new portfolio_format_leap2a_entry('forumpost' . $post->id, $post->subject, 'resource', $posthtml); 246 $entry->published = $post->created; 247 $entry->updated = $post->modified; 248 $entry->author = $post->author; 249 if (is_array($this->keyedfiles) && array_key_exists($post->id, $this->keyedfiles) && is_array($this->keyedfiles[$post->id])) { 250 $leapwriter->link_files($entry, $this->keyedfiles[$post->id], 'forumpost' . $post->id . 'attachment'); 251 } 252 $entry->add_category('web', 'resource_type'); 253 $leapwriter->add_entry($entry); 254 return $entry->id; 255 } 256 257 /** 258 * @param array $files 259 * @param mixed $justone false of id of single file to copy 260 * @return bool|void 261 */ 262 private function copy_files($files, $justone=false) { 263 if (empty($files)) { 264 return; 265 } 266 foreach ($files as $f) { 267 if ($justone && $f->get_id() != $justone) { 268 continue; 269 } 270 $this->get('exporter')->copy_existing_file($f); 271 if ($justone && $f->get_id() == $justone) { 272 return true; // all we need to do 273 } 274 } 275 } 276 /** 277 * this is a very cut down version of what is in forum_make_mail_post 278 * 279 * @global object 280 * @param int $post 281 * @return string 282 */ 283 private function prepare_post($post, $fileoutputextras=null) { 284 global $DB; 285 static $users; 286 if (empty($users)) { 287 $users = array($this->user->id => $this->user); 288 } 289 if (!array_key_exists($post->userid, $users)) { 290 $users[$post->userid] = $DB->get_record('user', array('id' => $post->userid)); 291 } 292 // add the user object on to the post so we can pass it to the leap writer if necessary 293 $post->author = $users[$post->userid]; 294 $viewfullnames = true; 295 // format the post body 296 $options = portfolio_format_text_options(); 297 $format = $this->get('exporter')->get('format'); 298 $formattedtext = format_text($post->message, $post->messageformat, $options, $this->get('course')->id); 299 $formattedtext = portfolio_rewrite_pluginfile_urls($formattedtext, $this->modcontext->id, 'mod_forum', 'post', $post->id, $format); 300 301 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">'; 302 303 $output .= '<tr class="header"><td>';// can't print picture. 304 $output .= '</td>'; 305 306 if ($post->parent) { 307 $output .= '<td class="topic">'; 308 } else { 309 $output .= '<td class="topic starter">'; 310 } 311 $output .= '<div class="subject">'.format_string($post->subject).'</div>'; 312 313 $fullname = fullname($users[$post->userid], $viewfullnames); 314 $by = new stdClass(); 315 $by->name = $fullname; 316 $by->date = userdate($post->modified, '', core_date::get_user_timezone($this->user)); 317 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>'; 318 319 $output .= '</td></tr>'; 320 321 $output .= '<tr><td class="left side" valign="top">'; 322 323 $output .= '</td><td class="content">'; 324 325 $output .= $formattedtext; 326 327 if (is_array($this->keyedfiles) && array_key_exists($post->id, $this->keyedfiles) && is_array($this->keyedfiles[$post->id]) && count($this->keyedfiles[$post->id]) > 0) { 328 $output .= '<div class="attachments">'; 329 $output .= '<br /><b>' . get_string('attachments', 'forum') . '</b>:<br /><br />'; 330 foreach ($this->keyedfiles[$post->id] as $file) { 331 $output .= $format->file_output($file) . '<br/ >'; 332 } 333 $output .= "</div>"; 334 } 335 336 $output .= '</td></tr></table>'."\n\n"; 337 338 return $output; 339 } 340 /** 341 * @return string 342 */ 343 function get_sha1() { 344 $filesha = ''; 345 try { 346 $filesha = $this->get_sha1_file(); 347 } catch (portfolio_caller_exception $e) { } // no files 348 349 if ($this->post) { 350 return sha1($filesha . ',' . $this->post->subject . ',' . $this->post->message); 351 } else { 352 $sha1s = array($filesha); 353 foreach ($this->posts as $post) { 354 $sha1s[] = sha1($post->subject . ',' . $post->message); 355 } 356 return sha1(implode(',', $sha1s)); 357 } 358 } 359 360 function expected_time() { 361 $filetime = $this->expected_time_file(); 362 if ($this->posts) { 363 $posttime = portfolio_expected_time_db(count($this->posts)); 364 if ($filetime < $posttime) { 365 return $posttime; 366 } 367 } 368 return $filetime; 369 } 370 /** 371 * @uses CONTEXT_MODULE 372 * @return bool 373 */ 374 function check_permissions() { 375 $context = context_module::instance($this->cm->id); 376 if ($this->post) { 377 return (has_capability('mod/forum:exportpost', $context) 378 || ($this->post->userid == $this->user->id 379 && has_capability('mod/forum:exportownpost', $context))); 380 } 381 return has_capability('mod/forum:exportdiscussion', $context); 382 } 383 /** 384 * @return string 385 */ 386 public static function display_name() { 387 return get_string('modulename', 'forum'); 388 } 389 390 public static function base_supported_formats() { 391 return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A); 392 } 393 } 394 395 396 /** 397 * Class representing the virtual node with all itemids in the file browser 398 * 399 * @category files 400 * @copyright 2012 David Mudrak <david@moodle.com> 401 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 402 */ 403 class forum_file_info_container extends file_info { 404 /** @var file_browser */ 405 protected $browser; 406 /** @var stdClass */ 407 protected $course; 408 /** @var stdClass */ 409 protected $cm; 410 /** @var string */ 411 protected $component; 412 /** @var stdClass */ 413 protected $context; 414 /** @var array */ 415 protected $areas; 416 /** @var string */ 417 protected $filearea; 418 419 /** 420 * Constructor (in case you did not realize it ;-) 421 * 422 * @param file_browser $browser 423 * @param stdClass $course 424 * @param stdClass $cm 425 * @param stdClass $context 426 * @param array $areas 427 * @param string $filearea 428 */ 429 public function __construct($browser, $course, $cm, $context, $areas, $filearea) { 430 parent::__construct($browser, $context); 431 $this->browser = $browser; 432 $this->course = $course; 433 $this->cm = $cm; 434 $this->component = 'mod_forum'; 435 $this->context = $context; 436 $this->areas = $areas; 437 $this->filearea = $filearea; 438 } 439 440 /** 441 * @return array with keys contextid, filearea, itemid, filepath and filename 442 */ 443 public function get_params() { 444 return array( 445 'contextid' => $this->context->id, 446 'component' => $this->component, 447 'filearea' => $this->filearea, 448 'itemid' => null, 449 'filepath' => null, 450 'filename' => null, 451 ); 452 } 453 454 /** 455 * Can new files or directories be added via the file browser 456 * 457 * @return bool 458 */ 459 public function is_writable() { 460 return false; 461 } 462 463 /** 464 * Should this node be considered as a folder in the file browser 465 * 466 * @return bool 467 */ 468 public function is_directory() { 469 return true; 470 } 471 472 /** 473 * Returns localised visible name of this node 474 * 475 * @return string 476 */ 477 public function get_visible_name() { 478 return $this->areas[$this->filearea]; 479 } 480 481 /** 482 * Returns list of children nodes 483 * 484 * @return array of file_info instances 485 */ 486 public function get_children() { 487 return $this->get_filtered_children('*', false, true); 488 } 489 /** 490 * Help function to return files matching extensions or their count 491 * 492 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 493 * @param bool|int $countonly if false returns the children, if an int returns just the 494 * count of children but stops counting when $countonly number of children is reached 495 * @param bool $returnemptyfolders if true returns items that don't have matching files inside 496 * @return array|int array of file_info instances or the count 497 */ 498 private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) { 499 global $DB; 500 $params = array('contextid' => $this->context->id, 501 'component' => $this->component, 502 'filearea' => $this->filearea); 503 $sql = 'SELECT DISTINCT itemid 504 FROM {files} 505 WHERE contextid = :contextid 506 AND component = :component 507 AND filearea = :filearea'; 508 if (!$returnemptyfolders) { 509 $sql .= ' AND filename <> :emptyfilename'; 510 $params['emptyfilename'] = '.'; 511 } 512 list($sql2, $params2) = $this->build_search_files_sql($extensions); 513 $sql .= ' '.$sql2; 514 $params = array_merge($params, $params2); 515 if ($countonly !== false) { 516 $sql .= ' ORDER BY itemid DESC'; 517 } 518 519 $rs = $DB->get_recordset_sql($sql, $params); 520 $children = array(); 521 foreach ($rs as $record) { 522 if (($child = $this->browser->get_file_info($this->context, 'mod_forum', $this->filearea, $record->itemid)) 523 && ($returnemptyfolders || $child->count_non_empty_children($extensions))) { 524 $children[] = $child; 525 } 526 if ($countonly !== false && count($children) >= $countonly) { 527 break; 528 } 529 } 530 $rs->close(); 531 if ($countonly !== false) { 532 return count($children); 533 } 534 return $children; 535 } 536 537 /** 538 * Returns list of children which are either files matching the specified extensions 539 * or folders that contain at least one such file. 540 * 541 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 542 * @return array of file_info instances 543 */ 544 public function get_non_empty_children($extensions = '*') { 545 return $this->get_filtered_children($extensions, false); 546 } 547 548 /** 549 * Returns the number of children which are either files matching the specified extensions 550 * or folders containing at least one such file. 551 * 552 * @param string|array $extensions, for example '*' or array('.gif','.jpg') 553 * @param int $limit stop counting after at least $limit non-empty children are found 554 * @return int 555 */ 556 public function count_non_empty_children($extensions = '*', $limit = 1) { 557 return $this->get_filtered_children($extensions, $limit); 558 } 559 560 /** 561 * Returns parent file_info instance 562 * 563 * @return file_info or null for root 564 */ 565 public function get_parent() { 566 return $this->browser->get_file_info($this->context); 567 } 568 }
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 |