[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * Functions and classes for commenting 19 * 20 * @package core 21 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 defined('MOODLE_INTERNAL') || die(); 25 26 /** 27 * Comment is helper class to add/delete comments anywhere in moodle 28 * 29 * @package core 30 * @category comment 31 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org} 32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 */ 34 class comment { 35 /** @var int there may be several comment box in one page so we need a client_id to recognize them */ 36 private $cid; 37 /** @var string commentarea is used to specify different parts shared the same itemid */ 38 private $commentarea; 39 /** @var int itemid is used to associate with commenting content */ 40 private $itemid; 41 /** @var string this html snippet will be used as a template to build comment content */ 42 private $template; 43 /** @var int The context id for comments */ 44 private $contextid; 45 /** @var stdClass The context itself */ 46 private $context; 47 /** @var int The course id for comments */ 48 private $courseid; 49 /** @var stdClass course module object, only be used to help find pluginname automatically */ 50 private $cm; 51 /** 52 * The component that this comment is for. 53 * 54 * It is STRONGLY recommended to set this. 55 * Added as a database field in 2.9, old comments will have a null component. 56 * 57 * @var string 58 */ 59 private $component; 60 /** @var string This is calculated by normalising the component */ 61 private $pluginname; 62 /** @var string This is calculated by normalising the component */ 63 private $plugintype; 64 /** @var bool Whether the user has the required capabilities/permissions to view comments. */ 65 private $viewcap = false; 66 /** @var bool Whether the user has the required capabilities/permissions to post comments. */ 67 private $postcap = false; 68 /** @var string to customize link text */ 69 private $linktext; 70 /** @var bool If set to true then comment sections won't be able to be opened and closed instead they will always be visible. */ 71 protected $notoggle = false; 72 /** @var bool If set to true comments are automatically loaded as soon as the page loads. */ 73 protected $autostart = false; 74 /** @var bool If set to true the total count of comments is displayed when displaying comments. */ 75 protected $displaytotalcount = false; 76 /** @var bool If set to true a cancel button will be shown on the form used to submit comments. */ 77 protected $displaycancel = false; 78 /** @var int The number of comments associated with this comments params */ 79 protected $totalcommentcount = null; 80 81 /** 82 * Set to true to remove the col attribute from the textarea making it full width. 83 * @var bool 84 */ 85 protected $fullwidth = false; 86 87 /** @var bool Use non-javascript UI */ 88 private static $nonjs = false; 89 /** @var int comment itemid used in non-javascript UI */ 90 private static $comment_itemid = null; 91 /** @var int comment context used in non-javascript UI */ 92 private static $comment_context = null; 93 /** @var string comment area used in non-javascript UI */ 94 private static $comment_area = null; 95 /** @var string comment page used in non-javascript UI */ 96 private static $comment_page = null; 97 /** @var string comment itemid component in non-javascript UI */ 98 private static $comment_component = null; 99 100 /** 101 * Construct function of comment class, initialise 102 * class members 103 * 104 * @param stdClass $options { 105 * context => context context to use for the comment [required] 106 * component => string which plugin will comment being added to [required] 107 * itemid => int the id of the associated item (forum post, glossary item etc) [required] 108 * area => string comment area 109 * cm => stdClass course module 110 * course => course course object 111 * client_id => string an unique id to identify comment area 112 * autostart => boolean automatically expend comments 113 * showcount => boolean display the number of comments 114 * displaycancel => boolean display cancel button 115 * notoggle => boolean don't show/hide button 116 * linktext => string title of show/hide button 117 * } 118 */ 119 public function __construct(stdClass $options) { 120 $this->viewcap = false; 121 $this->postcap = false; 122 123 // setup client_id 124 if (!empty($options->client_id)) { 125 $this->cid = $options->client_id; 126 } else { 127 $this->cid = uniqid(); 128 } 129 130 // setup context 131 if (!empty($options->context)) { 132 $this->context = $options->context; 133 $this->contextid = $this->context->id; 134 } else if(!empty($options->contextid)) { 135 $this->contextid = $options->contextid; 136 $this->context = context::instance_by_id($this->contextid); 137 } else { 138 print_error('invalidcontext'); 139 } 140 141 if (!empty($options->component)) { 142 // set and validate component 143 $this->set_component($options->component); 144 } else { 145 // component cannot be empty 146 throw new comment_exception('invalidcomponent'); 147 } 148 149 // setup course 150 // course will be used to generate user profile link 151 if (!empty($options->course)) { 152 $this->courseid = $options->course->id; 153 } else if (!empty($options->courseid)) { 154 $this->courseid = $options->courseid; 155 } else { 156 if ($coursecontext = $this->context->get_course_context(false)) { 157 $this->courseid = $coursecontext->instanceid; 158 } else { 159 $this->courseid = SITEID; 160 } 161 } 162 163 // setup coursemodule 164 if (!empty($options->cm)) { 165 $this->cm = $options->cm; 166 } else { 167 $this->cm = null; 168 } 169 170 // setup commentarea 171 if (!empty($options->area)) { 172 $this->commentarea = $options->area; 173 } 174 175 // setup itemid 176 if (!empty($options->itemid)) { 177 $this->itemid = $options->itemid; 178 } else { 179 $this->itemid = 0; 180 } 181 182 // setup customized linktext 183 if (!empty($options->linktext)) { 184 $this->linktext = $options->linktext; 185 } else { 186 $this->linktext = get_string('comments'); 187 } 188 189 // setup options for callback functions 190 $this->comment_param = new stdClass(); 191 $this->comment_param->context = $this->context; 192 $this->comment_param->courseid = $this->courseid; 193 $this->comment_param->cm = $this->cm; 194 $this->comment_param->commentarea = $this->commentarea; 195 $this->comment_param->itemid = $this->itemid; 196 197 // setup notoggle 198 if (!empty($options->notoggle)) { 199 $this->set_notoggle($options->notoggle); 200 } 201 202 // setup notoggle 203 if (!empty($options->autostart)) { 204 $this->set_autostart($options->autostart); 205 } 206 207 // setup displaycancel 208 if (!empty($options->displaycancel)) { 209 $this->set_displaycancel($options->displaycancel); 210 } 211 212 // setup displaytotalcount 213 if (!empty($options->showcount)) { 214 $this->set_displaytotalcount($options->showcount); 215 } 216 217 // setting post and view permissions 218 $this->check_permissions(); 219 220 // load template 221 $this->template = html_writer::start_tag('div', array('class' => 'comment-message')); 222 223 $this->template .= html_writer::start_tag('div', array('class' => 'comment-message-meta')); 224 225 $this->template .= html_writer::tag('span', '___picture___', array('class' => 'picture')); 226 $this->template .= html_writer::tag('span', '___name___', array('class' => 'user')) . ' - '; 227 $this->template .= html_writer::tag('span', '___time___', array('class' => 'time')); 228 229 $this->template .= html_writer::end_tag('div'); // .comment-message-meta 230 $this->template .= html_writer::tag('div', '___content___', array('class' => 'text')); 231 232 $this->template .= html_writer::end_tag('div'); // .comment-message 233 234 if (!empty($this->plugintype)) { 235 $this->template = plugin_callback($this->plugintype, $this->pluginname, 'comment', 'template', array($this->comment_param), $this->template); 236 } 237 238 unset($options); 239 } 240 241 /** 242 * Receive nonjs comment parameters 243 * 244 * @param moodle_page $page The page object to initialise comments within 245 * If not provided the global $PAGE is used 246 */ 247 public static function init(moodle_page $page = null) { 248 global $PAGE; 249 250 if (empty($page)) { 251 $page = $PAGE; 252 } 253 // setup variables for non-js interface 254 self::$nonjs = optional_param('nonjscomment', '', PARAM_ALPHANUM); 255 self::$comment_itemid = optional_param('comment_itemid', '', PARAM_INT); 256 self::$comment_component = optional_param('comment_component', '', PARAM_COMPONENT); 257 self::$comment_context = optional_param('comment_context', '', PARAM_INT); 258 self::$comment_page = optional_param('comment_page', '', PARAM_INT); 259 self::$comment_area = optional_param('comment_area', '', PARAM_AREA); 260 261 $page->requires->strings_for_js(array( 262 'addcomment', 263 'comments', 264 'commentscount', 265 'commentsrequirelogin', 266 'deletecomment', 267 ), 268 'moodle' 269 ); 270 } 271 272 /** 273 * Sets the component. 274 * 275 * This method shouldn't be public, changing the component once it has been set potentially 276 * invalidates permission checks. 277 * A coding_error is now thrown if code attempts to change the component. 278 * 279 * @throws coding_exception if you try to change the component after it has been set. 280 * @param string $component 281 */ 282 public function set_component($component) { 283 if (!empty($this->component) && $this->component !== $component) { 284 throw new coding_exception('You cannot change the component of a comment once it has been set'); 285 } 286 $this->component = $component; 287 list($this->plugintype, $this->pluginname) = core_component::normalize_component($component); 288 } 289 290 /** 291 * Determines if the user can view the comment. 292 * 293 * @param bool $value 294 */ 295 public function set_view_permission($value) { 296 $this->viewcap = (bool)$value; 297 } 298 299 /** 300 * Determines if the user can post a comment 301 * 302 * @param bool $value 303 */ 304 public function set_post_permission($value) { 305 $this->postcap = (bool)$value; 306 } 307 308 /** 309 * check posting comments permission 310 * It will check based on user roles and ask modules 311 * If you need to check permission by modules, a 312 * function named $pluginname_check_comment_post must be implemented 313 */ 314 private function check_permissions() { 315 $this->postcap = has_capability('moodle/comment:post', $this->context); 316 $this->viewcap = has_capability('moodle/comment:view', $this->context); 317 if (!empty($this->plugintype)) { 318 $permissions = plugin_callback($this->plugintype, $this->pluginname, 'comment', 'permissions', array($this->comment_param), array('post'=>false, 'view'=>false)); 319 $this->postcap = $this->postcap && $permissions['post']; 320 $this->viewcap = $this->viewcap && $permissions['view']; 321 } 322 } 323 324 /** 325 * Gets a link for this page that will work with JS disabled. 326 * 327 * @global moodle_page $PAGE 328 * @param moodle_page $page 329 * @return moodle_url 330 */ 331 public function get_nojslink(moodle_page $page = null) { 332 if ($page === null) { 333 global $PAGE; 334 $page = $PAGE; 335 } 336 337 $link = new moodle_url($page->url, array( 338 'nonjscomment' => true, 339 'comment_itemid' => $this->itemid, 340 'comment_context' => $this->context->id, 341 'comment_component' => $this->get_component(), 342 'comment_area' => $this->commentarea, 343 )); 344 $link->remove_params(array('comment_page')); 345 return $link; 346 } 347 348 /** 349 * Sets the value of the notoggle option. 350 * 351 * If set to true then the user will not be able to expand and collase 352 * the comment section. 353 * 354 * @param bool $newvalue 355 */ 356 public function set_notoggle($newvalue = true) { 357 $this->notoggle = (bool)$newvalue; 358 } 359 360 /** 361 * Sets the value of the autostart option. 362 * 363 * If set to true then the comments will be loaded during page load. 364 * Normally this happens only once the user expands the comment section. 365 * 366 * @param bool $newvalue 367 */ 368 public function set_autostart($newvalue = true) { 369 $this->autostart = (bool)$newvalue; 370 } 371 372 /** 373 * Sets the displaycancel option 374 * 375 * If set to true then a cancel button will be shown when using the form 376 * to post comments. 377 * 378 * @param bool $newvalue 379 */ 380 public function set_displaycancel($newvalue = true) { 381 $this->displaycancel = (bool)$newvalue; 382 } 383 384 /** 385 * Sets the displaytotalcount option 386 * 387 * If set to true then the total number of comments will be displayed 388 * when printing comments. 389 * 390 * @param bool $newvalue 391 */ 392 public function set_displaytotalcount($newvalue = true) { 393 $this->displaytotalcount = (bool)$newvalue; 394 } 395 396 /** 397 * Initialises the JavaScript that enchances the comment API. 398 * 399 * @param moodle_page $page The moodle page object that the JavaScript should be 400 * initialised for. 401 */ 402 public function initialise_javascript(moodle_page $page) { 403 404 $options = new stdClass; 405 $options->client_id = $this->cid; 406 $options->commentarea = $this->commentarea; 407 $options->itemid = $this->itemid; 408 $options->page = 0; 409 $options->courseid = $this->courseid; 410 $options->contextid = $this->contextid; 411 $options->component = $this->component; 412 $options->notoggle = $this->notoggle; 413 $options->autostart = $this->autostart; 414 415 $page->requires->js_init_call('M.core_comment.init', array($options), true); 416 417 return true; 418 } 419 420 /** 421 * Prepare comment code in html 422 * @param boolean $return 423 * @return string|void 424 */ 425 public function output($return = true) { 426 global $PAGE, $OUTPUT; 427 static $template_printed; 428 429 $this->initialise_javascript($PAGE); 430 431 if (!empty(self::$nonjs)) { 432 // return non js comments interface 433 return $this->print_comments(self::$comment_page, $return, true); 434 } 435 436 $html = ''; 437 438 // print html template 439 // Javascript will use the template to render new comments 440 if (empty($template_printed) && $this->can_view()) { 441 $html .= html_writer::tag('div', $this->template, array('style' => 'display:none', 'id' => 'cmt-tmpl')); 442 $template_printed = true; 443 } 444 445 if ($this->can_view()) { 446 // print commenting icon and tooltip 447 $html .= html_writer::start_tag('div', array('class' => 'mdl-left')); 448 $html .= html_writer::link($this->get_nojslink($PAGE), get_string('showcommentsnonjs'), array('class' => 'showcommentsnonjs')); 449 450 if (!$this->notoggle) { 451 // If toggling is enabled (notoggle=false) then print the controls to toggle 452 // comments open and closed 453 $countstring = ''; 454 if ($this->displaytotalcount) { 455 $countstring = '('.$this->count().')'; 456 } 457 $collapsedimage= 't/collapsed'; 458 if (right_to_left()) { 459 $collapsedimage= 't/collapsed_rtl'; 460 } else { 461 $collapsedimage= 't/collapsed'; 462 } 463 $html .= html_writer::start_tag('a', array('class' => 'comment-link', 'id' => 'comment-link-'.$this->cid, 'href' => '#')); 464 $html .= html_writer::empty_tag('img', array('id' => 'comment-img-'.$this->cid, 'src' => $OUTPUT->pix_url($collapsedimage), 'alt' => $this->linktext, 'title' => $this->linktext)); 465 $html .= html_writer::tag('span', $this->linktext.' '.$countstring, array('id' => 'comment-link-text-'.$this->cid)); 466 $html .= html_writer::end_tag('a'); 467 } 468 469 $html .= html_writer::start_tag('div', array('id' => 'comment-ctrl-'.$this->cid, 'class' => 'comment-ctrl')); 470 471 if ($this->autostart) { 472 // If autostart has been enabled print the comments list immediatly 473 $html .= html_writer::start_tag('ul', array('id' => 'comment-list-'.$this->cid, 'class' => 'comment-list comments-loaded')); 474 $html .= html_writer::tag('li', '', array('class' => 'first')); 475 $html .= $this->print_comments(0, true, false); 476 $html .= html_writer::end_tag('ul'); // .comment-list 477 $html .= $this->get_pagination(0); 478 } else { 479 $html .= html_writer::start_tag('ul', array('id' => 'comment-list-'.$this->cid, 'class' => 'comment-list')); 480 $html .= html_writer::tag('li', '', array('class' => 'first')); 481 $html .= html_writer::end_tag('ul'); // .comment-list 482 $html .= html_writer::tag('div', '', array('id' => 'comment-pagination-'.$this->cid, 'class' => 'comment-pagination')); 483 } 484 485 if ($this->can_post()) { 486 // print posting textarea 487 $textareaattrs = array( 488 'name' => 'content', 489 'rows' => 2, 490 'id' => 'dlg-content-'.$this->cid 491 ); 492 if (!$this->fullwidth) { 493 $textareaattrs['cols'] = '20'; 494 } else { 495 $textareaattrs['class'] = 'fullwidth'; 496 } 497 498 $html .= html_writer::start_tag('div', array('class' => 'comment-area')); 499 $html .= html_writer::start_tag('div', array('class' => 'db')); 500 $html .= html_writer::tag('textarea', '', $textareaattrs); 501 $html .= html_writer::end_tag('div'); // .db 502 503 $html .= html_writer::start_tag('div', array('class' => 'fd', 'id' => 'comment-action-'.$this->cid)); 504 $html .= html_writer::link('#', get_string('savecomment'), array('id' => 'comment-action-post-'.$this->cid)); 505 506 if ($this->displaycancel) { 507 $html .= html_writer::tag('span', ' | '); 508 $html .= html_writer::link('#', get_string('cancel'), array('id' => 'comment-action-cancel-'.$this->cid)); 509 } 510 511 $html .= html_writer::end_tag('div'); // .fd 512 $html .= html_writer::end_tag('div'); // .comment-area 513 $html .= html_writer::tag('div', '', array('class' => 'clearer')); 514 } 515 516 $html .= html_writer::end_tag('div'); // .comment-ctrl 517 $html .= html_writer::end_tag('div'); // .mdl-left 518 } else { 519 $html = ''; 520 } 521 522 if ($return) { 523 return $html; 524 } else { 525 echo $html; 526 } 527 } 528 529 /** 530 * Return matched comments 531 * 532 * @param int $page 533 * @return array 534 */ 535 public function get_comments($page = '') { 536 global $DB, $CFG, $USER, $OUTPUT; 537 if (!$this->can_view()) { 538 return false; 539 } 540 if (!is_numeric($page)) { 541 $page = 0; 542 } 543 $params = array(); 544 $perpage = (!empty($CFG->commentsperpage))?$CFG->commentsperpage:15; 545 $start = $page * $perpage; 546 $ufields = user_picture::fields('u'); 547 548 list($componentwhere, $component) = $this->get_component_select_sql('c'); 549 if ($component) { 550 $params['component'] = $component; 551 } 552 553 $sql = "SELECT $ufields, c.id AS cid, c.content AS ccontent, c.format AS cformat, c.timecreated AS ctimecreated 554 FROM {comments} c 555 JOIN {user} u ON u.id = c.userid 556 WHERE c.contextid = :contextid AND 557 c.commentarea = :commentarea AND 558 c.itemid = :itemid AND 559 $componentwhere 560 ORDER BY c.timecreated DESC"; 561 $params['contextid'] = $this->contextid; 562 $params['commentarea'] = $this->commentarea; 563 $params['itemid'] = $this->itemid; 564 565 $comments = array(); 566 $formatoptions = array('overflowdiv' => true); 567 $rs = $DB->get_recordset_sql($sql, $params, $start, $perpage); 568 foreach ($rs as $u) { 569 $c = new stdClass(); 570 $c->id = $u->cid; 571 $c->content = $u->ccontent; 572 $c->format = $u->cformat; 573 $c->timecreated = $u->ctimecreated; 574 $c->strftimeformat = get_string('strftimerecentfull', 'langconfig'); 575 $url = new moodle_url('/user/view.php', array('id'=>$u->id, 'course'=>$this->courseid)); 576 $c->profileurl = $url->out(false); // URL should not be escaped just yet. 577 $c->fullname = fullname($u); 578 $c->time = userdate($c->timecreated, $c->strftimeformat); 579 $c->content = format_text($c->content, $c->format, $formatoptions); 580 $c->avatar = $OUTPUT->user_picture($u, array('size'=>18)); 581 $c->userid = $u->id; 582 583 $candelete = $this->can_delete($c->id); 584 if (($USER->id == $u->id) || !empty($candelete)) { 585 $c->delete = true; 586 } 587 $comments[] = $c; 588 } 589 $rs->close(); 590 591 if (!empty($this->plugintype)) { 592 // moodle module will filter comments 593 $comments = plugin_callback($this->plugintype, $this->pluginname, 'comment', 'display', array($comments, $this->comment_param), $comments); 594 } 595 596 return $comments; 597 } 598 599 /** 600 * Returns an SQL fragment and param for selecting on component. 601 * @param string $alias 602 * @return array 603 */ 604 protected function get_component_select_sql($alias = '') { 605 $component = $this->get_component(); 606 if ($alias) { 607 $alias = $alias.'.'; 608 } 609 if (empty($component)) { 610 $componentwhere = "{$alias}component IS NULL"; 611 $component = null; 612 } else { 613 $componentwhere = "({$alias}component IS NULL OR {$alias}component = :component)"; 614 } 615 return array($componentwhere, $component); 616 } 617 618 /** 619 * Returns the number of comments associated with the details of this object 620 * 621 * @global moodle_database $DB 622 * @return int 623 */ 624 public function count() { 625 global $DB; 626 if ($this->totalcommentcount === null) { 627 list($where, $component) = $this->get_component_select_sql(); 628 $where .= ' AND itemid = :itemid AND commentarea = :commentarea AND contextid = :contextid'; 629 $params = array( 630 'itemid' => $this->itemid, 631 'commentarea' => $this->commentarea, 632 'contextid' => $this->context->id, 633 ); 634 if ($component) { 635 $params['component'] = $component; 636 } 637 638 $this->totalcommentcount = $DB->count_records_select('comments', $where, $params); 639 } 640 return $this->totalcommentcount; 641 } 642 643 /** 644 * Returns HTML to display a pagination bar 645 * 646 * @global stdClass $CFG 647 * @global core_renderer $OUTPUT 648 * @param int $page 649 * @return string 650 */ 651 public function get_pagination($page = 0) { 652 global $CFG, $OUTPUT; 653 $count = $this->count(); 654 $perpage = (!empty($CFG->commentsperpage))?$CFG->commentsperpage:15; 655 $pages = (int)ceil($count/$perpage); 656 if ($pages == 1 || $pages == 0) { 657 return html_writer::tag('div', '', array('id' => 'comment-pagination-'.$this->cid, 'class' => 'comment-pagination')); 658 } 659 if (!empty(self::$nonjs)) { 660 // used in non-js interface 661 return $OUTPUT->paging_bar($count, $page, $perpage, $this->get_nojslink(), 'comment_page'); 662 } else { 663 // return ajax paging bar 664 $str = ''; 665 $str .= '<div class="comment-paging" id="comment-pagination-'.$this->cid.'">'; 666 for ($p=0; $p<$pages; $p++) { 667 if ($p == $page) { 668 $class = 'curpage'; 669 } else { 670 $class = 'pageno'; 671 } 672 $str .= '<a href="#" class="'.$class.'" id="comment-page-'.$this->cid.'-'.$p.'">'.($p+1).'</a> '; 673 } 674 $str .= '</div>'; 675 } 676 return $str; 677 } 678 679 /** 680 * Add a new comment 681 * 682 * @global moodle_database $DB 683 * @param string $content 684 * @param int $format 685 * @return stdClass 686 */ 687 public function add($content, $format = FORMAT_MOODLE) { 688 global $CFG, $DB, $USER, $OUTPUT; 689 if (!$this->can_post()) { 690 throw new comment_exception('nopermissiontocomment'); 691 } 692 $now = time(); 693 $newcmt = new stdClass; 694 $newcmt->contextid = $this->contextid; 695 $newcmt->commentarea = $this->commentarea; 696 $newcmt->itemid = $this->itemid; 697 $newcmt->component = !empty($this->component) ? $this->component : null; 698 $newcmt->content = $content; 699 $newcmt->format = $format; 700 $newcmt->userid = $USER->id; 701 $newcmt->timecreated = $now; 702 703 // This callback allow module to modify the content of comment, such as filter or replacement 704 plugin_callback($this->plugintype, $this->pluginname, 'comment', 'add', array(&$newcmt, $this->comment_param)); 705 706 $cmt_id = $DB->insert_record('comments', $newcmt); 707 if (!empty($cmt_id)) { 708 $newcmt->id = $cmt_id; 709 $newcmt->strftimeformat = get_string('strftimerecent', 'langconfig'); 710 $newcmt->fullname = fullname($USER); 711 $url = new moodle_url('/user/view.php', array('id' => $USER->id, 'course' => $this->courseid)); 712 $newcmt->profileurl = $url->out(); 713 $newcmt->content = format_text($newcmt->content, $newcmt->format, array('overflowdiv'=>true)); 714 $newcmt->avatar = $OUTPUT->user_picture($USER, array('size'=>16)); 715 716 $commentlist = array($newcmt); 717 718 if (!empty($this->plugintype)) { 719 // Call the display callback to allow the plugin to format the newly added comment. 720 $commentlist = plugin_callback($this->plugintype, 721 $this->pluginname, 722 'comment', 723 'display', 724 array($commentlist, $this->comment_param), 725 $commentlist); 726 $newcmt = $commentlist[0]; 727 } 728 $newcmt->time = userdate($newcmt->timecreated, $newcmt->strftimeformat); 729 730 // Trigger comment created event. 731 if (core_component::is_core_subsystem($this->component)) { 732 $eventclassname = '\\core\\event\\' . $this->component . '_comment_created'; 733 } else { 734 $eventclassname = '\\' . $this->component . '\\event\comment_created'; 735 } 736 if (class_exists($eventclassname)) { 737 $event = $eventclassname::create( 738 array( 739 'context' => $this->context, 740 'objectid' => $newcmt->id, 741 'other' => array( 742 'itemid' => $this->itemid 743 ) 744 )); 745 $event->trigger(); 746 } 747 748 return $newcmt; 749 } else { 750 throw new comment_exception('dbupdatefailed'); 751 } 752 } 753 754 /** 755 * delete by context, commentarea and itemid 756 * @param stdClass|array $param { 757 * contextid => int the context in which the comments exist [required] 758 * commentarea => string the comment area [optional] 759 * itemid => int comment itemid [optional] 760 * } 761 * @return boolean 762 */ 763 public static function delete_comments($param) { 764 global $DB; 765 $param = (array)$param; 766 if (empty($param['contextid'])) { 767 return false; 768 } 769 $DB->delete_records('comments', $param); 770 return true; 771 } 772 773 /** 774 * Delete page_comments in whole course, used by course reset 775 * 776 * @param stdClass $context course context 777 */ 778 public static function reset_course_page_comments($context) { 779 global $DB; 780 $contexts = array(); 781 $contexts[] = $context->id; 782 $children = $context->get_child_contexts(); 783 foreach ($children as $c) { 784 $contexts[] = $c->id; 785 } 786 list($ids, $params) = $DB->get_in_or_equal($contexts); 787 $DB->delete_records_select('comments', "commentarea='page_comments' AND contextid $ids", $params); 788 } 789 790 /** 791 * Delete a comment 792 * 793 * @param int $commentid 794 * @return bool 795 */ 796 public function delete($commentid) { 797 global $DB, $USER; 798 $candelete = has_capability('moodle/comment:delete', $this->context); 799 if (!$comment = $DB->get_record('comments', array('id'=>$commentid))) { 800 throw new comment_exception('dbupdatefailed'); 801 } 802 if (!($USER->id == $comment->userid || !empty($candelete))) { 803 throw new comment_exception('nopermissiontocomment'); 804 } 805 $DB->delete_records('comments', array('id'=>$commentid)); 806 // Trigger comment delete event. 807 if (core_component::is_core_subsystem($this->component)) { 808 $eventclassname = '\\core\\event\\' . $this->component . '_comment_deleted'; 809 } else { 810 $eventclassname = '\\' . $this->component . '\\event\comment_deleted'; 811 } 812 if (class_exists($eventclassname)) { 813 $event = $eventclassname::create( 814 array( 815 'context' => $this->context, 816 'objectid' => $commentid, 817 'other' => array( 818 'itemid' => $this->itemid 819 ) 820 )); 821 $event->add_record_snapshot('comments', $comment); 822 $event->trigger(); 823 } 824 return true; 825 } 826 827 /** 828 * Print comments 829 * 830 * @param int $page 831 * @param bool $return return comments list string or print it out 832 * @param bool $nonjs print nonjs comments list or not? 833 * @return string|void 834 */ 835 public function print_comments($page = 0, $return = true, $nonjs = true) { 836 global $DB, $CFG, $PAGE; 837 838 if (!$this->can_view()) { 839 return ''; 840 } 841 842 if (!(self::$comment_itemid == $this->itemid && 843 self::$comment_context == $this->context->id && 844 self::$comment_area == $this->commentarea && 845 self::$comment_component == $this->component 846 )) { 847 $page = 0; 848 } 849 $comments = $this->get_comments($page); 850 851 $html = ''; 852 if ($nonjs) { 853 $html .= html_writer::tag('h3', get_string('comments')); 854 $html .= html_writer::start_tag('ul', array('id' => 'comment-list-'.$this->cid, 'class' => 'comment-list')); 855 } 856 // Reverse the comments array to display them in the correct direction 857 foreach (array_reverse($comments) as $cmt) { 858 $html .= html_writer::tag('li', $this->print_comment($cmt, $nonjs), array('id' => 'comment-'.$cmt->id.'-'.$this->cid)); 859 } 860 if ($nonjs) { 861 $html .= html_writer::end_tag('ul'); 862 $html .= $this->get_pagination($page); 863 } 864 if ($nonjs && $this->can_post()) { 865 // Form to add comments 866 $html .= html_writer::start_tag('form', array('method' => 'post', 'action' => new moodle_url('/comment/comment_post.php'))); 867 // Comment parameters 868 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'contextid', 'value' => $this->contextid)); 869 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'add')); 870 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'area', 'value' => $this->commentarea)); 871 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'component', 'value' => $this->component)); 872 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'itemid', 'value' => $this->itemid)); 873 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'courseid', 'value' => $this->courseid)); 874 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); 875 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'returnurl', 'value' => $PAGE->url)); 876 // Textarea for the actual comment 877 $html .= html_writer::tag('textarea', '', array('name' => 'content', 'rows' => 2)); 878 // Submit button to add the comment 879 $html .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('submit'))); 880 $html .= html_writer::end_tag('form'); 881 } 882 if ($return) { 883 return $html; 884 } else { 885 echo $html; 886 } 887 } 888 889 /** 890 * Returns an array containing comments in HTML format. 891 * 892 * @global core_renderer $OUTPUT 893 * @param stdClass $cmt { 894 * id => int comment id 895 * content => string comment content 896 * format => int comment text format 897 * timecreated => int comment's timecreated 898 * profileurl => string link to user profile 899 * fullname => comment author's full name 900 * avatar => string user's avatar 901 * delete => boolean does user have permission to delete comment? 902 * } 903 * @param bool $nonjs 904 * @return array 905 */ 906 public function print_comment($cmt, $nonjs = true) { 907 global $OUTPUT; 908 $patterns = array(); 909 $replacements = array(); 910 911 if (!empty($cmt->delete) && empty($nonjs)) { 912 $deletelink = html_writer::start_tag('div', array('class'=>'comment-delete')); 913 $deletelink .= html_writer::start_tag('a', array('href' => '#', 'id' => 'comment-delete-'.$this->cid.'-'.$cmt->id)); 914 $deletelink .= $OUTPUT->pix_icon('t/delete', get_string('delete')); 915 $deletelink .= html_writer::end_tag('a'); 916 $deletelink .= html_writer::end_tag('div'); 917 $cmt->content = $deletelink . $cmt->content; 918 } 919 $patterns[] = '___picture___'; 920 $patterns[] = '___name___'; 921 $patterns[] = '___content___'; 922 $patterns[] = '___time___'; 923 $replacements[] = $cmt->avatar; 924 $replacements[] = html_writer::link($cmt->profileurl, $cmt->fullname); 925 $replacements[] = $cmt->content; 926 $replacements[] = $cmt->time; 927 928 // use html template to format a single comment. 929 return str_replace($patterns, $replacements, $this->template); 930 } 931 932 /** 933 * Revoke validate callbacks 934 * 935 * @param stdClass $params addtionall parameters need to add to callbacks 936 */ 937 protected function validate($params=array()) { 938 foreach ($params as $key=>$value) { 939 $this->comment_param->$key = $value; 940 } 941 $validation = plugin_callback($this->plugintype, $this->pluginname, 'comment', 'validate', array($this->comment_param), false); 942 if (!$validation) { 943 throw new comment_exception('invalidcommentparam'); 944 } 945 } 946 947 /** 948 * Returns true if the user is able to view comments 949 * @return bool 950 */ 951 public function can_view() { 952 $this->validate(); 953 return !empty($this->viewcap); 954 } 955 956 /** 957 * Returns true if the user can add comments against this comment description 958 * @return bool 959 */ 960 public function can_post() { 961 $this->validate(); 962 return isloggedin() && !empty($this->postcap); 963 } 964 965 /** 966 * Returns true if the user can delete this comment 967 * @param int $commentid 968 * @return bool 969 */ 970 public function can_delete($commentid) { 971 $this->validate(array('commentid'=>$commentid)); 972 return has_capability('moodle/comment:delete', $this->context); 973 } 974 975 /** 976 * Returns the component associated with the comment. 977 * 978 * @return string 979 */ 980 public function get_component() { 981 return $this->component; 982 } 983 984 /** 985 * Do not call! I am a deprecated method because of the typo in my name. 986 * @deprecated since 2.9 987 * @see comment::get_component() 988 * @return string 989 */ 990 public function get_compontent() { 991 return $this->get_component(); 992 } 993 994 /** 995 * Returns the context associated with the comment 996 * @return stdClass 997 */ 998 public function get_context() { 999 return $this->context; 1000 } 1001 1002 /** 1003 * Returns the course id associated with the comment 1004 * @return int 1005 */ 1006 public function get_courseid() { 1007 return $this->courseid; 1008 } 1009 1010 /** 1011 * Returns the course module associated with the comment 1012 * 1013 * @return stdClass 1014 */ 1015 public function get_cm() { 1016 return $this->cm; 1017 } 1018 1019 /** 1020 * Returns the item id associated with the comment 1021 * 1022 * @return int 1023 */ 1024 public function get_itemid() { 1025 return $this->itemid; 1026 } 1027 1028 /** 1029 * Returns the comment area associated with the commentarea 1030 * 1031 * @return stdClass 1032 */ 1033 public function get_commentarea() { 1034 return $this->commentarea; 1035 } 1036 1037 /** 1038 * Make the comments textarea fullwidth. 1039 * 1040 * @since 2.8.1 + 2.7.4 1041 * @param bool $fullwidth 1042 */ 1043 public function set_fullwidth($fullwidth = true) { 1044 $this->fullwidth = (bool)$fullwidth; 1045 } 1046 1047 /** 1048 * Return the template. 1049 * 1050 * @since 3.1 1051 * @return string 1052 */ 1053 public function get_template() { 1054 return $this->template; 1055 } 1056 1057 /** 1058 * Return the cid. 1059 * 1060 * @since 3.1 1061 * @return string 1062 */ 1063 public function get_cid() { 1064 return $this->cid; 1065 } 1066 1067 /** 1068 * Return the link text. 1069 * 1070 * @since 3.1 1071 * @return string 1072 */ 1073 public function get_linktext() { 1074 return $this->linktext; 1075 } 1076 1077 /** 1078 * Return no toggle. 1079 * 1080 * @since 3.1 1081 * @return bool 1082 */ 1083 public function get_notoggle() { 1084 return $this->notoggle; 1085 } 1086 1087 /** 1088 * Return display total count. 1089 * 1090 * @since 3.1 1091 * @return bool 1092 */ 1093 public function get_displaytotalcount() { 1094 return $this->displaytotalcount; 1095 } 1096 1097 /** 1098 * Return display cancel. 1099 * 1100 * @since 3.1 1101 * @return bool 1102 */ 1103 public function get_displaycancel() { 1104 return $this->displaycancel; 1105 } 1106 1107 /** 1108 * Return fullwidth. 1109 * 1110 * @since 3.1 1111 * @return bool 1112 */ 1113 public function get_fullwidth() { 1114 return $this->fullwidth; 1115 } 1116 1117 /** 1118 * Return autostart. 1119 * 1120 * @since 3.1 1121 * @return bool 1122 */ 1123 public function get_autostart() { 1124 return $this->autostart; 1125 } 1126 1127 } 1128 1129 /** 1130 * Comment exception class 1131 * 1132 * @package core 1133 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org} 1134 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1135 */ 1136 class comment_exception extends moodle_exception { 1137 }
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 |