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