Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Classes for Blogs. 19 * 20 * @package moodlecore 21 * @subpackage blog 22 * @copyright 2009 Nicolas Connault 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once($CFG->libdir . '/filelib.php'); 29 30 /** 31 * Blog_entry class. Represents an entry in a user's blog. Contains all methods for managing this entry. 32 * This class does not contain any HTML-generating code. See blog_listing sub-classes for such code. 33 * This class follows the Object Relational Mapping technique, its member variables being mapped to 34 * the fields of the post table. 35 * 36 * @package moodlecore 37 * @subpackage blog 38 * @copyright 2009 Nicolas Connault 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class blog_entry implements renderable { 42 // Public Database fields. 43 public $id; 44 public $userid; 45 public $subject; 46 public $summary; 47 public $rating = 0; 48 public $attachment; 49 public $publishstate; 50 51 // Locked Database fields (Don't touch these). 52 public $courseid = 0; 53 public $groupid = 0; 54 public $module = 'blog'; 55 public $moduleid = 0; 56 public $coursemoduleid = 0; 57 public $content; 58 public $format = 1; 59 public $uniquehash = ''; 60 public $lastmodified; 61 public $created; 62 public $usermodified; 63 64 // Other class variables. 65 public $form; 66 public $tags = array(); 67 68 /** @var StdClass Data needed to render the entry */ 69 public $renderable; 70 71 /** @var string summary format. */ 72 public string $summaryformat; 73 74 /** @var array summary editor. */ 75 public array $summary_editor; 76 77 /** @var string */ 78 public $summarytrust; 79 80 /** @var int course associated with the blog post. */ 81 public $courseassoc; 82 83 /** @var string module associated with the blog post. */ 84 public $modassoc; 85 86 /** @var mixed attachment. */ 87 public $attachment_filemanager; 88 89 /** @var string blog post body. */ 90 public $body; 91 92 /** @var int attachment entry id. */ 93 public $entryid; 94 95 /** @var string|null submit button. */ 96 public $submitbutton; 97 98 /** @var string|null user alias. */ 99 public $useridalias; 100 101 /** @var string|null user picture. */ 102 public $picture; 103 104 /** @var string|null user first name. */ 105 public $firstname; 106 107 /** @var string|null user middle name. */ 108 public $middlename; 109 110 /** @var string|null user last name. */ 111 public $lastname; 112 113 /** @var string|null user first name phonetic. */ 114 public $firstnamephonetic; 115 116 /** @var string|null user last name phonetic. */ 117 public $lastnamephonetic; 118 119 /** @var string|null user alternate name. */ 120 public $alternatename; 121 122 /** @var string|null user email address. */ 123 public $email; 124 125 /** @var string */ 126 public $action; 127 128 /** @var string|null user picture description. */ 129 public $imagealt; 130 131 /** @var int module instance id. */ 132 public $modid; 133 134 /** 135 * Constructor. If given an id, will fetch the corresponding record from the DB. 136 * 137 * @param mixed $idorparams A blog entry id if INT, or data for a new entry if array 138 */ 139 public function __construct($id=null, $params=null, $form=null) { 140 global $DB, $PAGE, $CFG; 141 142 if (!empty($id)) { 143 $object = $DB->get_record('post', array('id' => $id)); 144 foreach ($object as $var => $val) { 145 $this->$var = $val; 146 } 147 } else if (!empty($params) && (is_array($params) || is_object($params))) { 148 foreach ($params as $var => $val) { 149 $this->$var = $val; 150 } 151 } 152 153 if (!empty($CFG->useblogassociations)) { 154 $associations = $DB->get_records('blog_association', array('blogid' => $this->id)); 155 foreach ($associations as $association) { 156 $context = context::instance_by_id($association->contextid); 157 if ($context->contextlevel == CONTEXT_COURSE) { 158 $this->courseassoc = $association->contextid; 159 } else if ($context->contextlevel == CONTEXT_MODULE) { 160 $this->modassoc = $association->contextid; 161 } 162 } 163 } 164 165 $this->form = $form; 166 } 167 168 169 /** 170 * Gets the required data to print the entry 171 */ 172 public function prepare_render() { 173 174 global $DB, $CFG, $PAGE; 175 176 $this->renderable = new StdClass(); 177 178 $this->renderable->user = $DB->get_record('user', array('id' => $this->userid)); 179 180 // Entry comments. 181 if (!empty($CFG->usecomments) and $CFG->blogusecomments) { 182 require_once($CFG->dirroot . '/comment/lib.php'); 183 184 $cmt = new stdClass(); 185 $cmt->context = context_user::instance($this->userid); 186 $cmt->courseid = $PAGE->course->id; 187 $cmt->area = 'format_blog'; 188 $cmt->itemid = $this->id; 189 $cmt->showcount = $CFG->blogshowcommentscount; 190 $cmt->component = 'blog'; 191 $this->renderable->comment = new comment($cmt); 192 } 193 194 $this->summary = file_rewrite_pluginfile_urls($this->summary, 'pluginfile.php', SYSCONTEXTID, 'blog', 'post', $this->id); 195 196 // External blog link. 197 if ($this->uniquehash && $this->content) { 198 if ($externalblog = $DB->get_record('blog_external', array('id' => $this->content))) { 199 $urlparts = parse_url($externalblog->url); 200 $this->renderable->externalblogtext = get_string('retrievedfrom', 'blog') . get_string('labelsep', 'langconfig'); 201 $this->renderable->externalblogtext .= html_writer::link($urlparts['scheme'] . '://' . $urlparts['host'], 202 $externalblog->name); 203 } 204 } 205 206 // Retrieve associations. 207 $this->renderable->unassociatedentry = false; 208 if (!empty($CFG->useblogassociations)) { 209 210 // Adding the entry associations data. 211 if ($associations = $associations = $DB->get_records('blog_association', array('blogid' => $this->id))) { 212 213 // Check to see if the entry is unassociated with group/course level access. 214 if ($this->publishstate == 'group' || $this->publishstate == 'course') { 215 $this->renderable->unassociatedentry = true; 216 } 217 218 foreach ($associations as $key => $assocrec) { 219 220 if (!$context = context::instance_by_id($assocrec->contextid, IGNORE_MISSING)) { 221 unset($associations[$key]); 222 continue; 223 } 224 225 // The renderer will need the contextlevel of the association. 226 $associations[$key]->contextlevel = $context->contextlevel; 227 228 // Course associations. 229 if ($context->contextlevel == CONTEXT_COURSE) { 230 // TODO: performance!!!! 231 $instancename = $DB->get_field('course', 'shortname', array('id' => $context->instanceid)); 232 233 $associations[$key]->url = $assocurl = new moodle_url('/course/view.php', 234 array('id' => $context->instanceid)); 235 $associations[$key]->text = $instancename; 236 $associations[$key]->icon = new pix_icon('i/course', $associations[$key]->text); 237 } 238 239 // Mod associations. 240 if ($context->contextlevel == CONTEXT_MODULE) { 241 242 // Getting the activity type and the activity instance id. 243 $sql = 'SELECT cm.instance, m.name FROM {course_modules} cm 244 JOIN {modules} m ON m.id = cm.module 245 WHERE cm.id = :cmid'; 246 $modinfo = $DB->get_record_sql($sql, array('cmid' => $context->instanceid)); 247 // TODO: performance!!!! 248 $instancename = $DB->get_field($modinfo->name, 'name', array('id' => $modinfo->instance)); 249 250 $associations[$key]->type = get_string('modulename', $modinfo->name); 251 $associations[$key]->url = new moodle_url('/mod/' . $modinfo->name . '/view.php', 252 array('id' => $context->instanceid)); 253 $associations[$key]->text = $instancename; 254 $associations[$key]->icon = new pix_icon('icon', $associations[$key]->text, $modinfo->name); 255 } 256 } 257 } 258 $this->renderable->blogassociations = $associations; 259 } 260 261 // Entry attachments. 262 $this->renderable->attachments = $this->get_attachments(); 263 264 $this->renderable->usercanedit = blog_user_can_edit_entry($this); 265 } 266 267 268 /** 269 * Gets the entry attachments list 270 * @return array List of blog_entry_attachment instances 271 */ 272 public function get_attachments() { 273 274 global $CFG; 275 276 require_once($CFG->libdir.'/filelib.php'); 277 278 $syscontext = context_system::instance(); 279 280 $fs = get_file_storage(); 281 $files = $fs->get_area_files($syscontext->id, 'blog', 'attachment', $this->id); 282 283 // Adding a blog_entry_attachment for each non-directory file. 284 $attachments = array(); 285 foreach ($files as $file) { 286 if ($file->is_directory()) { 287 continue; 288 } 289 $attachments[] = new blog_entry_attachment($file, $this->id); 290 } 291 292 return $attachments; 293 } 294 295 /** 296 * Inserts this entry in the database. Access control checks must be done by calling code. 297 * 298 * @param mform $form Used for attachments 299 * @return void 300 */ 301 public function process_attachment($form) { 302 $this->form = $form; 303 } 304 305 /** 306 * Inserts this entry in the database. Access control checks must be done by calling code. 307 * TODO Set the publishstate correctly 308 * @return void 309 */ 310 public function add() { 311 global $CFG, $USER, $DB; 312 313 unset($this->id); 314 $this->module = 'blog'; 315 $this->userid = (empty($this->userid)) ? $USER->id : $this->userid; 316 $this->lastmodified = time(); 317 $this->created = time(); 318 319 // Insert the new blog entry. 320 $this->id = $DB->insert_record('post', $this); 321 322 if (!empty($CFG->useblogassociations)) { 323 $this->add_associations(); 324 } 325 326 core_tag_tag::set_item_tags('core', 'post', $this->id, context_user::instance($this->userid), $this->tags); 327 328 // Trigger an event for the new entry. 329 $event = \core\event\blog_entry_created::create(array( 330 'objectid' => $this->id, 331 'relateduserid' => $this->userid 332 )); 333 $event->set_blog_entry($this); 334 $event->trigger(); 335 } 336 337 /** 338 * Updates this entry in the database. Access control checks must be done by calling code. 339 * 340 * @param array $params Entry parameters. 341 * @param moodleform $form Used for attachments. 342 * @param array $summaryoptions Summary options. 343 * @param array $attachmentoptions Attachment options. 344 * 345 * @return void 346 */ 347 public function edit($params=array(), $form=null, $summaryoptions=array(), $attachmentoptions=array()) { 348 global $CFG, $DB; 349 350 $sitecontext = context_system::instance(); 351 $entry = $this; 352 353 $this->form = $form; 354 foreach ($params as $var => $val) { 355 $entry->$var = $val; 356 } 357 358 $entry = file_postupdate_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog', 'post', $entry->id); 359 $entry = file_postupdate_standard_filemanager($entry, 360 'attachment', 361 $attachmentoptions, 362 $sitecontext, 363 'blog', 364 'attachment', 365 $entry->id); 366 367 if (!empty($CFG->useblogassociations)) { 368 $entry->add_associations(); 369 } 370 371 $entry->lastmodified = time(); 372 373 // Update record. 374 $DB->update_record('post', $entry); 375 core_tag_tag::set_item_tags('core', 'post', $entry->id, context_user::instance($this->userid), $entry->tags); 376 377 $event = \core\event\blog_entry_updated::create(array( 378 'objectid' => $entry->id, 379 'relateduserid' => $entry->userid 380 )); 381 $event->set_blog_entry($entry); 382 $event->trigger(); 383 } 384 385 /** 386 * Deletes this entry from the database. Access control checks must be done by calling code. 387 * 388 * @return void 389 */ 390 public function delete() { 391 global $DB; 392 393 $this->delete_attachments(); 394 $this->remove_associations(); 395 396 // Get record to pass onto the event. 397 $record = $DB->get_record('post', array('id' => $this->id)); 398 $DB->delete_records('post', array('id' => $this->id)); 399 core_tag_tag::remove_all_item_tags('core', 'post', $this->id); 400 401 $event = \core\event\blog_entry_deleted::create(array( 402 'objectid' => $this->id, 403 'relateduserid' => $this->userid 404 )); 405 $event->add_record_snapshot("post", $record); 406 $event->set_blog_entry($this); 407 $event->trigger(); 408 } 409 410 /** 411 * Function to add all context associations to an entry. 412 * 413 * @param string $unused This does nothing, do not use it. 414 */ 415 public function add_associations($unused = null) { 416 417 if ($unused !== null) { 418 debugging('Illegal argument used in blog_entry->add_associations()', DEBUG_DEVELOPER); 419 } 420 421 $this->remove_associations(); 422 423 if (!empty($this->courseassoc)) { 424 $this->add_association($this->courseassoc); 425 } 426 427 if (!empty($this->modassoc)) { 428 $this->add_association($this->modassoc); 429 } 430 } 431 432 /** 433 * Add a single association for a blog entry 434 * 435 * @param int $contextid - id of context to associate with the blog entry. 436 * @param string $unused This does nothing, do not use it. 437 */ 438 public function add_association($contextid, $unused = null) { 439 global $DB; 440 441 if ($unused !== null) { 442 debugging('Illegal argument used in blog_entry->add_association()', DEBUG_DEVELOPER); 443 } 444 445 $assocobject = new StdClass; 446 $assocobject->contextid = $contextid; 447 $assocobject->blogid = $this->id; 448 $id = $DB->insert_record('blog_association', $assocobject); 449 450 // Trigger an association created event. 451 $context = context::instance_by_id($contextid); 452 $eventparam = array( 453 'objectid' => $id, 454 'other' => array('associateid' => $context->instanceid, 'subject' => $this->subject, 'blogid' => $this->id), 455 'relateduserid' => $this->userid 456 ); 457 if ($context->contextlevel == CONTEXT_COURSE) { 458 $eventparam['other']['associatetype'] = 'course'; 459 460 } else if ($context->contextlevel == CONTEXT_MODULE) { 461 $eventparam['other']['associatetype'] = 'coursemodule'; 462 } 463 $event = \core\event\blog_association_created::create($eventparam); 464 $event->trigger(); 465 } 466 467 /** 468 * remove all associations for a blog entry 469 * 470 * @return void 471 */ 472 public function remove_associations() { 473 global $DB; 474 475 $associations = $DB->get_records('blog_association', array('blogid' => $this->id)); 476 foreach ($associations as $association) { 477 478 // Trigger an association deleted event. 479 $context = context::instance_by_id($association->contextid); 480 $eventparam = array( 481 'objectid' => $this->id, 482 'other' => array('subject' => $this->subject, 'blogid' => $this->id), 483 'relateduserid' => $this->userid 484 ); 485 $event = \core\event\blog_association_deleted::create($eventparam); 486 $event->add_record_snapshot('blog_association', $association); 487 $event->trigger(); 488 489 // Now remove the association. 490 $DB->delete_records('blog_association', array('id' => $association->id)); 491 } 492 } 493 494 /** 495 * Deletes all the user files in the attachments area for an entry 496 * 497 * @return void 498 */ 499 public function delete_attachments() { 500 $fs = get_file_storage(); 501 $fs->delete_area_files(SYSCONTEXTID, 'blog', 'attachment', $this->id); 502 $fs->delete_area_files(SYSCONTEXTID, 'blog', 'post', $this->id); 503 } 504 505 /** 506 * User can edit a blog entry if this is their own blog entry and they have 507 * the capability moodle/blog:create, or if they have the capability 508 * moodle/blog:manageentries. 509 * This also applies to deleting of entries. 510 * 511 * @param int $userid Optional. If not given, $USER is used 512 * @return boolean 513 */ 514 public function can_user_edit($userid=null) { 515 global $CFG, $USER; 516 517 if (empty($userid)) { 518 $userid = $USER->id; 519 } 520 521 $sitecontext = context_system::instance(); 522 523 if (has_capability('moodle/blog:manageentries', $sitecontext)) { 524 return true; // Can edit any blog entry. 525 } 526 527 if ($this->userid == $userid && has_capability('moodle/blog:create', $sitecontext)) { 528 return true; // Can edit own when having blog:create capability. 529 } 530 531 return false; 532 } 533 534 /** 535 * Checks to see if a user can view the blogs of another user. 536 * Only blog level is checked here, the capabilities are enforced 537 * in blog/index.php 538 * 539 * @param int $targetuserid ID of the user we are checking 540 * 541 * @return bool 542 */ 543 public function can_user_view($targetuserid) { 544 global $CFG, $USER, $DB; 545 $sitecontext = context_system::instance(); 546 547 if (empty($CFG->enableblogs) || !has_capability('moodle/blog:view', $sitecontext)) { 548 return false; // Blog system disabled or user has no blog view capability. 549 } 550 551 if (isloggedin() && $USER->id == $targetuserid) { 552 return true; // Can view own entries in any case. 553 } 554 555 if (has_capability('moodle/blog:manageentries', $sitecontext)) { 556 return true; // Can manage all entries. 557 } 558 559 // Coming for 1 entry, make sure it's not a draft. 560 if ($this->publishstate == 'draft' && !has_capability('moodle/blog:viewdrafts', $sitecontext)) { 561 return false; // Can not view draft of others. 562 } 563 564 // Coming for 1 entry, make sure user is logged in, if not a public blog. 565 if ($this->publishstate != 'public' && !isloggedin()) { 566 return false; 567 } 568 569 switch ($CFG->bloglevel) { 570 case BLOG_GLOBAL_LEVEL: 571 return true; 572 break; 573 574 case BLOG_SITE_LEVEL: 575 if (isloggedin()) { // Not logged in viewers forbidden. 576 return true; 577 } 578 return false; 579 break; 580 581 case BLOG_USER_LEVEL: 582 default: 583 $personalcontext = context_user::instance($targetuserid); 584 return has_capability('moodle/user:readuserblogs', $personalcontext); 585 break; 586 } 587 } 588 589 /** 590 * Use this function to retrieve a list of publish states available for 591 * the currently logged in user. 592 * 593 * @return array This function returns an array ideal for sending to moodles' 594 * choose_from_menu function. 595 */ 596 597 public static function get_applicable_publish_states() { 598 global $CFG; 599 $options = array(); 600 601 // Everyone gets draft access. 602 if ($CFG->bloglevel >= BLOG_USER_LEVEL) { 603 $options['draft'] = get_string('publishtonoone', 'blog'); 604 } 605 606 if ($CFG->bloglevel > BLOG_USER_LEVEL) { 607 $options['site'] = get_string('publishtosite', 'blog'); 608 } 609 610 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { 611 $options['public'] = get_string('publishtoworld', 'blog'); 612 } 613 614 return $options; 615 } 616 } 617 618 /** 619 * Abstract Blog_Listing class: used to gather blog entries and output them as listings. One of the subclasses must be used. 620 * 621 * @package moodlecore 622 * @subpackage blog 623 * @copyright 2009 Nicolas Connault 624 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 625 */ 626 class blog_listing { 627 /** 628 * Array of blog_entry objects. 629 * @var array $entries 630 */ 631 public $entries = null; 632 633 /** 634 * Caches the total number of the entries. 635 * @var int 636 */ 637 public $totalentries = null; 638 639 /** 640 * An array of blog_filter_* objects 641 * @var array $filters 642 */ 643 public $filters = array(); 644 645 /** 646 * Constructor 647 * 648 * @param array $filters An associative array of filtername => filterid 649 */ 650 public function __construct($filters=array()) { 651 // Unset filters overridden by more specific filters. 652 foreach ($filters as $type => $id) { 653 if (!empty($type) && !empty($id)) { 654 $this->filters[$type] = blog_filter::get_instance($id, $type); 655 } 656 } 657 658 foreach ($this->filters as $type => $filter) { 659 foreach ($filter->overrides as $override) { 660 if (array_key_exists($override, $this->filters)) { 661 unset($this->filters[$override]); 662 } 663 } 664 } 665 } 666 667 /** 668 * Fetches the array of blog entries. 669 * 670 * @return array 671 */ 672 public function get_entries($start=0, $limit=10) { 673 global $DB; 674 675 if ($this->entries === null) { 676 if ($sqlarray = $this->get_entry_fetch_sql(false, 'created DESC')) { 677 $this->entries = $DB->get_records_sql($sqlarray['sql'], $sqlarray['params'], $start, $limit); 678 if (!$start && count($this->entries) < $limit) { 679 $this->totalentries = count($this->entries); 680 } 681 } else { 682 return false; 683 } 684 } 685 686 return $this->entries; 687 } 688 689 /** 690 * Finds total number of blog entries 691 * 692 * @return int 693 */ 694 public function count_entries() { 695 global $DB; 696 if ($this->totalentries === null) { 697 if ($sqlarray = $this->get_entry_fetch_sql(true)) { 698 $this->totalentries = $DB->count_records_sql($sqlarray['sql'], $sqlarray['params']); 699 } else { 700 $this->totalentries = 0; 701 } 702 } 703 return $this->totalentries; 704 } 705 706 public function get_entry_fetch_sql($count=false, $sort='lastmodified DESC', $userid = false) { 707 global $DB, $USER, $CFG; 708 709 if (!$userid) { 710 $userid = $USER->id; 711 } 712 $userfieldsapi = \core_user\fields::for_userpic(); 713 $allnamefields = $userfieldsapi->get_sql('u', false, '', 'useridalias', false)->selects; 714 // The query used to locate blog entries is complicated. It will be built from the following components: 715 $requiredfields = "p.*, $allnamefields"; // The SELECT clause. 716 $tables = array('p' => 'post', 'u' => 'user'); // Components of the FROM clause (table_id => table_name). 717 // Components of the WHERE clause (conjunction). 718 $conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')'); 719 720 // Build up a clause for permission constraints. 721 722 $params = array(); 723 724 // Fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs. 725 // Admins can see all blogs regardless of publish states, as described on the help page. 726 if (has_capability('moodle/user:readuserblogs', context_system::instance())) { 727 // Don't add permission constraints. 728 729 } else if (!empty($this->filters['user']) 730 && has_capability('moodle/user:readuserblogs', 731 context_user::instance((empty($this->filters['user']->id) ? 0 : $this->filters['user']->id)))) { 732 // Don't add permission constraints. 733 734 } else { 735 if (isloggedin() and !isguestuser()) { 736 // Dont check association records if there aren't any. 737 $assocexists = $DB->record_exists('blog_association', array()); 738 739 // Begin permission sql clause. 740 $permissionsql = '(p.userid = ? '; 741 $params[] = $userid; 742 743 if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // Add permission to view site-level entries. 744 $permissionsql .= " OR p.publishstate = 'site' "; 745 } 746 747 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // Add permission to view global entries. 748 $permissionsql .= " OR p.publishstate = 'public' "; 749 } 750 751 $permissionsql .= ') '; // Close permissions sql clause. 752 } else { // Default is access to public entries. 753 $permissionsql = "p.publishstate = 'public'"; 754 } 755 $conditions[] = $permissionsql; // Add permission constraints. 756 } 757 758 foreach ($this->filters as $type => $blogfilter) { 759 $conditions = array_merge($conditions, $blogfilter->conditions); 760 $params = array_merge($params, $blogfilter->params); 761 $tables = array_merge($tables, $blogfilter->tables); 762 } 763 764 $tablessql = ''; // Build up the FROM clause. 765 foreach ($tables as $tablename => $table) { 766 $tablessql .= ($tablessql ? ', ' : '').'{'.$table.'} '.$tablename; 767 } 768 769 $sql = ($count) ? 'SELECT COUNT(*)' : 'SELECT ' . $requiredfields; 770 $sql .= " FROM $tablessql WHERE " . implode(' AND ', $conditions); 771 $sql .= ($count) ? '' : " ORDER BY $sort"; 772 773 return array('sql' => $sql, 'params' => $params); 774 } 775 776 /** 777 * Outputs all the blog entries aggregated by this blog listing. 778 * 779 * @return void 780 */ 781 public function print_entries() { 782 global $CFG, $USER, $DB, $OUTPUT, $PAGE; 783 $sitecontext = context_system::instance(); 784 785 // Blog renderer. 786 $output = $PAGE->get_renderer('blog'); 787 788 $page = optional_param('blogpage', 0, PARAM_INT); 789 $limit = optional_param('limit', get_user_preferences('blogpagesize', 10), PARAM_INT); 790 $start = $page * $limit; 791 792 $morelink = '<br /> '; 793 794 $entries = $this->get_entries($start, $limit); 795 $totalentries = $this->count_entries(); 796 $pagingbar = new paging_bar($totalentries, $page, $limit, $this->get_baseurl()); 797 $pagingbar->pagevar = 'blogpage'; 798 $blogheaders = blog_get_headers(); 799 800 echo $OUTPUT->render($pagingbar); 801 802 if (has_capability('moodle/blog:create', $sitecontext)) { 803 // The user's blog is enabled and they are viewing their own blog. 804 $userid = optional_param('userid', null, PARAM_INT); 805 806 if (empty($userid) || (!empty($userid) && $userid == $USER->id)) { 807 808 $courseid = optional_param('courseid', null, PARAM_INT); 809 $modid = optional_param('modid', null, PARAM_INT); 810 811 $addurl = new moodle_url("$CFG->wwwroot/blog/edit.php"); 812 $urlparams = array('action' => 'add', 813 'userid' => $userid, 814 'courseid' => $courseid, 815 'groupid' => optional_param('groupid', null, PARAM_INT), 816 'modid' => $modid, 817 'tagid' => optional_param('tagid', null, PARAM_INT), 818 'tag' => optional_param('tag', null, PARAM_INT), 819 'search' => optional_param('search', null, PARAM_INT)); 820 821 $urlparams = array_filter($urlparams); 822 $addurl->params($urlparams); 823 824 $addlink = '<div class="addbloglink">'; 825 $addlink .= '<a href="'.$addurl->out().'">'. $blogheaders['stradd'].'</a>'; 826 $addlink .= '</div>'; 827 echo $addlink; 828 } 829 } 830 831 if ($entries) { 832 $count = 0; 833 foreach ($entries as $entry) { 834 $blogentry = new blog_entry(null, $entry); 835 836 // Get the required blog entry data to render it. 837 $blogentry->prepare_render(); 838 echo $output->render($blogentry); 839 840 $count++; 841 } 842 843 echo $OUTPUT->render($pagingbar); 844 845 if (!$count) { 846 print '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />'; 847 } 848 849 print $morelink.'<br />'."\n"; 850 return; 851 } 852 } 853 854 // Find the base url from $_GET variables, for print_paging_bar. 855 public function get_baseurl() { 856 $getcopy = $_GET; 857 858 unset($getcopy['blogpage']); 859 860 if (!empty($getcopy)) { 861 $first = false; 862 $querystring = ''; 863 864 foreach ($getcopy as $var => $val) { 865 if (!$first) { 866 $first = true; 867 $querystring .= "?$var=$val"; 868 } else { 869 $querystring .= '&'.$var.'='.$val; 870 $hasparam = true; 871 } 872 } 873 } else { 874 $querystring = '?'; 875 } 876 877 return strip_querystring(qualified_me()) . $querystring; 878 879 } 880 } 881 882 /** 883 * Abstract class for blog_filter objects. 884 * A set of core filters are implemented here. To write new filters, you need to subclass 885 * blog_filter and give it the name of the type you want (for example, blog_filter_entry). 886 * The blog_filter abstract class will automatically use it when the filter is added to the 887 * URL. The first parameter of the constructor is the ID of your filter, but it can be a string 888 * or have any other meaning you wish it to have. The second parameter is called $type and is 889 * used as a sub-type for filters that have a very similar implementation (see blog_filter_context for an example) 890 */ 891 abstract class blog_filter { 892 /** 893 * An array of strings representing the available filter types for each blog_filter. 894 * @var array $availabletypes 895 */ 896 public $availabletypes = array(); 897 898 /** 899 * The type of filter (for example, types of blog_filter_context are site, course and module) 900 * @var string $type 901 */ 902 public $type; 903 904 /** 905 * The unique ID for a filter's associated record 906 * @var int $id 907 */ 908 public $id; 909 910 /** 911 * An array of table aliases that are used in the WHERE conditions 912 * @var array $tables 913 */ 914 public $tables = array(); 915 916 /** 917 * An array of WHERE conditions 918 * @var array $conditions 919 */ 920 public $conditions = array(); 921 922 /** 923 * An array of SQL params 924 * @var array $params 925 */ 926 public $params = array(); 927 928 /** 929 * An array of filter types which this particular filter type overrides: their conditions will not be evaluated 930 */ 931 public $overrides = array(); 932 933 public function __construct($id, $type=null) { 934 $this->id = $id; 935 $this->type = $type; 936 } 937 938 /** 939 * TODO This is poor design. A parent class should not know anything about its children. 940 * The default case helps to resolve this design issue 941 */ 942 public static function get_instance($id, $type) { 943 944 switch ($type) { 945 case 'site': 946 case 'course': 947 case 'module': 948 return new blog_filter_context($id, $type); 949 break; 950 951 case 'group': 952 case 'user': 953 return new blog_filter_user($id, $type); 954 break; 955 956 case 'tag': 957 return new blog_filter_tag($id); 958 break; 959 960 default: 961 $classname = "blog_filter_$type"; 962 if (class_exists($classname)) { 963 return new $classname($id, $type); 964 } 965 } 966 } 967 } 968 969 /** 970 * This filter defines the context level of the blog entries being searched: site, course, module 971 */ 972 class blog_filter_context extends blog_filter { 973 /** 974 * Constructor 975 * 976 * @param string $type 977 * @param int $id 978 */ 979 public function __construct($id=null, $type='site') { 980 global $SITE, $CFG, $DB; 981 982 if (empty($id)) { 983 $this->type = 'site'; 984 } else { 985 $this->id = $id; 986 $this->type = $type; 987 } 988 989 $this->availabletypes = array('site' => get_string('site'), 990 'course' => get_string('course'), 991 'module' => get_string('activity'), 992 'context' => get_string('coresystem')); 993 994 switch ($this->type) { 995 case 'course': // Careful of site course! 996 // Ignore course filter if blog associations are not enabled. 997 if ($this->id != $SITE->id && !empty($CFG->useblogassociations)) { 998 $this->overrides = array('site', 'context'); 999 $context = context_course::instance($this->id); 1000 $this->tables['ba'] = 'blog_association'; 1001 $this->conditions[] = 'p.id = ba.blogid'; 1002 $this->conditions[] = 'ba.contextid = '.$context->id; 1003 break; 1004 } else { 1005 // We are dealing with the site course, do not break from the current case. 1006 } 1007 1008 case 'site': 1009 // No special constraints. 1010 break; 1011 case 'module': 1012 if (!empty($CFG->useblogassociations)) { 1013 $this->overrides = array('course', 'site', 'context'); 1014 1015 $context = context_module::instance($this->id); 1016 $this->tables['ba'] = 'blog_association'; 1017 $this->tables['p'] = 'post'; 1018 $this->conditions = array('p.id = ba.blogid', 'ba.contextid = ?'); 1019 $this->params = array($context->id); 1020 } 1021 break; 1022 case 'context': 1023 if ($id != context_system::instance()->id && !empty($CFG->useblogassociations)) { 1024 $this->overrides = array('site'); 1025 $context = context::instance_by_id($this->id); 1026 $this->tables['ba'] = 'blog_association'; 1027 $this->tables['ctx'] = 'context'; 1028 $this->conditions[] = 'p.id = ba.blogid'; 1029 $this->conditions[] = 'ctx.id = ba.contextid'; 1030 $this->conditions[] = 'ctx.path LIKE ?'; 1031 $this->params = array($context->path . '%'); 1032 } 1033 break; 1034 1035 } 1036 } 1037 } 1038 1039 /** 1040 * This filter defines the user level of the blog entries being searched: a userid or a groupid. 1041 * It can be combined with a context filter in order to refine the search. 1042 */ 1043 class blog_filter_user extends blog_filter { 1044 public $tables = array('u' => 'user'); 1045 1046 /** 1047 * Constructor 1048 * 1049 * @param string $type 1050 * @param int $id 1051 */ 1052 public function __construct($id=null, $type='user') { 1053 global $CFG, $DB, $USER; 1054 $this->availabletypes = array('user' => get_string('user'), 'group' => get_string('group')); 1055 1056 if (empty($id)) { 1057 $this->id = $USER->id; 1058 $this->type = 'user'; 1059 } else { 1060 $this->id = $id; 1061 $this->type = $type; 1062 } 1063 1064 if ($this->type == 'user') { 1065 $this->conditions = array('u.id = ?'); 1066 $this->params = array($this->id); 1067 $this->overrides = array('group'); 1068 1069 } else if ($this->type == 'group') { 1070 $this->overrides = array('course', 'site'); 1071 1072 $this->tables['gm'] = 'groups_members'; 1073 $this->conditions[] = 'p.userid = gm.userid'; 1074 $this->conditions[] = 'gm.groupid = ?'; 1075 $this->params[] = $this->id; 1076 1077 if (!empty($CFG->useblogassociations)) { // Only show blog entries associated with this course. 1078 $coursecontext = context_course::instance($DB->get_field('groups', 'courseid', array('id' => $this->id))); 1079 $this->tables['ba'] = 'blog_association'; 1080 $this->conditions[] = 'gm.groupid = ?'; 1081 $this->conditions[] = 'ba.contextid = ?'; 1082 $this->conditions[] = 'ba.blogid = p.id'; 1083 $this->params[] = $this->id; 1084 $this->params[] = $coursecontext->id; 1085 } 1086 } 1087 1088 } 1089 } 1090 1091 /** 1092 * This filter defines a tag by which blog entries should be searched. 1093 */ 1094 class blog_filter_tag extends blog_filter { 1095 public $tables = array('t' => 'tag', 'ti' => 'tag_instance', 'p' => 'post'); 1096 1097 /** 1098 * Constructor 1099 * 1100 * @return void 1101 */ 1102 public function __construct($id) { 1103 global $DB; 1104 $this->id = $id; 1105 1106 $this->conditions = array('ti.tagid = t.id', 1107 "ti.itemtype = 'post'", 1108 "ti.component = 'core'", 1109 'ti.itemid = p.id', 1110 't.id = ?'); 1111 $this->params = array($this->id); 1112 } 1113 } 1114 1115 /** 1116 * This filter defines a specific blog entry id. 1117 */ 1118 class blog_filter_entry extends blog_filter { 1119 public $conditions = array('p.id = ?'); 1120 public $overrides = array('site', 'course', 'module', 'group', 'user', 'tag'); 1121 1122 public function __construct($id) { 1123 $this->id = $id; 1124 $this->params[] = $this->id; 1125 } 1126 } 1127 1128 /** 1129 * This filter restricts the results to a time interval in seconds up to time() 1130 */ 1131 class blog_filter_since extends blog_filter { 1132 public function __construct($interval) { 1133 $this->conditions[] = 'p.lastmodified >= ? AND p.lastmodified <= ?'; 1134 $this->params[] = time() - $interval; 1135 $this->params[] = time(); 1136 } 1137 } 1138 1139 /** 1140 * Filter used to perform full-text search on an entry's subject, summary and content 1141 */ 1142 class blog_filter_search extends blog_filter { 1143 1144 public function __construct($searchterm) { 1145 global $DB; 1146 $this->conditions = array("(".$DB->sql_like('p.summary', '?', false)." OR 1147 ".$DB->sql_like('p.content', '?', false)." OR 1148 ".$DB->sql_like('p.subject', '?', false).")"); 1149 $this->params[] = "%$searchterm%"; 1150 $this->params[] = "%$searchterm%"; 1151 $this->params[] = "%$searchterm%"; 1152 } 1153 } 1154 1155 1156 /** 1157 * Renderable class to represent an entry attachment 1158 */ 1159 class blog_entry_attachment implements renderable { 1160 1161 public $filename; 1162 public $url; 1163 public $file; 1164 1165 /** 1166 * Gets the file data 1167 * 1168 * @param stored_file $file 1169 * @param int $entryid Attachment entry id 1170 */ 1171 public function __construct($file, $entryid) { 1172 1173 global $CFG; 1174 1175 $this->file = $file; 1176 $this->filename = $file->get_filename(); 1177 $this->url = file_encode_url($CFG->wwwroot . '/pluginfile.php', 1178 '/' . SYSCONTEXTID . '/blog/attachment/' . $entryid . '/' . $this->filename); 1179 } 1180 1181 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body