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