2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Functions and classes for commenting
21 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 defined('MOODLE_INTERNAL') ||
die();
27 * Comment is helper class to add/delete comments anywhere in moodle
31 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 /** @var int there may be several comment box in one page so we need a client_id to recognize them */
37 /** @var string commentarea is used to specify different parts shared the same itemid */
39 /** @var int itemid is used to associate with commenting content */
41 /** @var string this html snippet will be used as a template to build comment content */
43 /** @var int The context id for comments */
45 /** @var stdClass The context itself */
47 /** @var int The course id for comments */
49 /** @var stdClass course module object, only be used to help find pluginname automatically */
51 /** @var string The component that this comment is for. It is STRONGLY recommended to set this. */
53 /** @var string This is calculated by normalising the component */
55 /** @var string This is calculated by normalising the component */
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 */
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;
74 /** @var bool Use non-javascript UI */
75 private static $nonjs = false;
76 /** @var int comment itemid used in non-javascript UI */
77 private static $comment_itemid = null;
78 /** @var int comment context used in non-javascript UI */
79 private static $comment_context = null;
80 /** @var string comment area used in non-javascript UI */
81 private static $comment_area = null;
82 /** @var string comment page used in non-javascript UI */
83 private static $comment_page = null;
84 /** @var string comment itemid component in non-javascript UI */
85 private static $comment_component = null;
88 * Construct function of comment class, initialise
91 * @param stdClass $options {
92 * context => context context to use for the comment [required]
93 * component => string which plugin will comment being added to [required]
94 * itemid => int the id of the associated item (forum post, glossary item etc) [required]
95 * area => string comment area
96 * cm => stdClass course module
97 * course => course course object
98 * client_id => string an unique id to identify comment area
99 * autostart => boolean automatically expend comments
100 * showcount => boolean display the number of comments
101 * displaycancel => boolean display cancel button
102 * notoggle => boolean don't show/hide button
103 * linktext => string title of show/hide button
106 public function __construct(stdClass
$options) {
107 $this->viewcap
= false;
108 $this->postcap
= false;
111 if (!empty($options->client_id
)) {
112 $this->cid
= $options->client_id
;
114 $this->cid
= uniqid();
118 if (!empty($options->context
)) {
119 $this->context
= $options->context
;
120 $this->contextid
= $this->context
->id
;
121 } else if(!empty($options->contextid
)) {
122 $this->contextid
= $options->contextid
;
123 $this->context
= context
::instance_by_id($this->contextid
);
125 print_error('invalidcontext');
128 if (!empty($options->component
)) {
129 // set and validate component
130 $this->set_component($options->component
);
132 // component cannot be empty
133 throw new comment_exception('invalidcomponent');
137 // course will be used to generate user profile link
138 if (!empty($options->course
)) {
139 $this->courseid
= $options->course
->id
;
140 } else if (!empty($options->courseid
)) {
141 $this->courseid
= $options->courseid
;
143 $this->courseid
= SITEID
;
146 // setup coursemodule
147 if (!empty($options->cm
)) {
148 $this->cm
= $options->cm
;
154 if (!empty($options->area
)) {
155 $this->commentarea
= $options->area
;
159 if (!empty($options->itemid
)) {
160 $this->itemid
= $options->itemid
;
165 // setup customized linktext
166 if (!empty($options->linktext
)) {
167 $this->linktext
= $options->linktext
;
169 $this->linktext
= get_string('comments');
172 // setup options for callback functions
173 $this->comment_param
= new stdClass();
174 $this->comment_param
->context
= $this->context
;
175 $this->comment_param
->courseid
= $this->courseid
;
176 $this->comment_param
->cm
= $this->cm
;
177 $this->comment_param
->commentarea
= $this->commentarea
;
178 $this->comment_param
->itemid
= $this->itemid
;
181 if (!empty($options->notoggle
)) {
182 $this->set_notoggle($options->notoggle
);
186 if (!empty($options->autostart
)) {
187 $this->set_autostart($options->autostart
);
190 // setup displaycancel
191 if (!empty($options->displaycancel
)) {
192 $this->set_displaycancel($options->displaycancel
);
195 // setup displaytotalcount
196 if (!empty($options->showcount
)) {
197 $this->set_displaytotalcount($options->showcount
);
200 // setting post and view permissions
201 $this->check_permissions();
204 $this->template
= html_writer
::start_tag('div', array('class' => 'comment-message'));
206 $this->template
.= html_writer
::start_tag('div', array('class' => 'comment-message-meta'));
208 $this->template
.= html_writer
::tag('span', '___picture___', array('class' => 'picture'));
209 $this->template
.= html_writer
::tag('span', '___name___', array('class' => 'user')) . ' - ';
210 $this->template
.= html_writer
::tag('span', '___time___', array('class' => 'time'));
212 $this->template
.= html_writer
::end_tag('div'); // .comment-message-meta
213 $this->template
.= html_writer
::tag('div', '___content___', array('class' => 'text'));
215 $this->template
.= html_writer
::end_tag('div'); // .comment-message
217 if (!empty($this->plugintype
)) {
218 $this->template
= plugin_callback($this->plugintype
, $this->pluginname
, 'comment', 'template', array($this->comment_param
), $this->template
);
225 * Receive nonjs comment parameters
227 * @param moodle_page $page The page object to initialise comments within
228 * If not provided the global $PAGE is used
230 public static function init(moodle_page
$page = null) {
236 // setup variables for non-js interface
237 self
::$nonjs = optional_param('nonjscomment', '', PARAM_ALPHANUM
);
238 self
::$comment_itemid = optional_param('comment_itemid', '', PARAM_INT
);
239 self
::$comment_context = optional_param('comment_context', '', PARAM_INT
);
240 self
::$comment_page = optional_param('comment_page', '', PARAM_INT
);
241 self
::$comment_area = optional_param('comment_area', '', PARAM_AREA
);
243 $page->requires
->string_for_js('addcomment', 'moodle');
244 $page->requires
->string_for_js('deletecomment', 'moodle');
245 $page->requires
->string_for_js('comments', 'moodle');
246 $page->requires
->string_for_js('commentsrequirelogin', 'moodle');
250 * Sets the component.
252 * This method shouldn't be public, changing the component once it has been set potentially
253 * invalidates permission checks.
254 * A coding_error is now thrown if code attempts to change the component.
256 * @param string $component
258 public function set_component($component) {
259 if (!empty($this->component
) && $this->component
!== $component) {
260 throw new coding_exception('You cannot change the component of a comment once it has been set');
262 $this->component
= $component;
263 list($this->plugintype
, $this->pluginname
) = core_component
::normalize_component($component);
267 * Determines if the user can view the comment.
271 public function set_view_permission($value) {
272 $this->viewcap
= (bool)$value;
276 * Determines if the user can post a comment
280 public function set_post_permission($value) {
281 $this->postcap
= (bool)$value;
285 * check posting comments permission
286 * It will check based on user roles and ask modules
287 * If you need to check permission by modules, a
288 * function named $pluginname_check_comment_post must be implemented
290 private function check_permissions() {
291 $this->postcap
= has_capability('moodle/comment:post', $this->context
);
292 $this->viewcap
= has_capability('moodle/comment:view', $this->context
);
293 if (!empty($this->plugintype
)) {
294 $permissions = plugin_callback($this->plugintype
, $this->pluginname
, 'comment', 'permissions', array($this->comment_param
), array('post'=>false, 'view'=>false));
295 $this->postcap
= $this->postcap
&& $permissions['post'];
296 $this->viewcap
= $this->viewcap
&& $permissions['view'];
301 * Gets a link for this page that will work with JS disabled.
303 * @global moodle_page $PAGE
304 * @param moodle_page $page
307 public function get_nojslink(moodle_page
$page = null) {
308 if ($page === null) {
313 $link = new moodle_url($page->url
, array(
314 'nonjscomment' => true,
315 'comment_itemid' => $this->itemid
,
316 'comment_context' => $this->context
->id
,
317 'comment_area' => $this->commentarea
,
319 $link->remove_params(array('comment_page'));
324 * Sets the value of the notoggle option.
326 * If set to true then the user will not be able to expand and collase
327 * the comment section.
329 * @param bool $newvalue
331 public function set_notoggle($newvalue = true) {
332 $this->notoggle
= (bool)$newvalue;
336 * Sets the value of the autostart option.
338 * If set to true then the comments will be loaded during page load.
339 * Normally this happens only once the user expands the comment section.
341 * @param bool $newvalue
343 public function set_autostart($newvalue = true) {
344 $this->autostart
= (bool)$newvalue;
348 * Sets the displaycancel option
350 * If set to true then a cancel button will be shown when using the form
353 * @param bool $newvalue
355 public function set_displaycancel($newvalue = true) {
356 $this->displaycancel
= (bool)$newvalue;
360 * Sets the displaytotalcount option
362 * If set to true then the total number of comments will be displayed
363 * when printing comments.
365 * @param bool $newvalue
367 public function set_displaytotalcount($newvalue = true) {
368 $this->displaytotalcount
= (bool)$newvalue;
372 * Initialises the JavaScript that enchances the comment API.
374 * @param moodle_page $page The moodle page object that the JavaScript should be
377 public function initialise_javascript(moodle_page
$page) {
379 $options = new stdClass
;
380 $options->client_id
= $this->cid
;
381 $options->commentarea
= $this->commentarea
;
382 $options->itemid
= $this->itemid
;
384 $options->courseid
= $this->courseid
;
385 $options->contextid
= $this->contextid
;
386 $options->component
= $this->component
;
387 $options->notoggle
= $this->notoggle
;
388 $options->autostart
= $this->autostart
;
390 $page->requires
->js_init_call('M.core_comment.init', array($options), true);
396 * Prepare comment code in html
397 * @param boolean $return
398 * @return string|void
400 public function output($return = true) {
401 global $PAGE, $OUTPUT;
402 static $template_printed;
404 $this->initialise_javascript($PAGE);
406 if (!empty(self
::$nonjs)) {
407 // return non js comments interface
408 return $this->print_comments(self
::$comment_page, $return, true);
413 // print html template
414 // Javascript will use the template to render new comments
415 if (empty($template_printed) && $this->can_view()) {
416 $html .= html_writer
::tag('div', $this->template
, array('style' => 'display:none', 'id' => 'cmt-tmpl'));
417 $template_printed = true;
420 if ($this->can_view()) {
421 // print commenting icon and tooltip
422 $html .= html_writer
::start_tag('div', array('class' => 'mdl-left'));
423 $html .= html_writer
::link($this->get_nojslink($PAGE), get_string('showcommentsnonjs'), array('class' => 'showcommentsnonjs'));
425 if (!$this->notoggle
) {
426 // If toggling is enabled (notoggle=false) then print the controls to toggle
427 // comments open and closed
429 if ($this->displaytotalcount
) {
430 $countstring = '('.$this->count().')';
432 $collapsedimage= 't/collapsed';
433 if (right_to_left()) {
434 $collapsedimage= 't/collapsed_rtl';
436 $collapsedimage= 't/collapsed';
438 $html .= html_writer
::start_tag('a', array('class' => 'comment-link', 'id' => 'comment-link-'.$this->cid
, 'href' => '#'));
439 $html .= html_writer
::empty_tag('img', array('id' => 'comment-img-'.$this->cid
, 'src' => $OUTPUT->pix_url($collapsedimage), 'alt' => $this->linktext
, 'title' => $this->linktext
));
440 $html .= html_writer
::tag('span', $this->linktext
.' '.$countstring, array('id' => 'comment-link-text-'.$this->cid
));
441 $html .= html_writer
::end_tag('a');
444 $html .= html_writer
::start_tag('div', array('id' => 'comment-ctrl-'.$this->cid
, 'class' => 'comment-ctrl'));
446 if ($this->autostart
) {
447 // If autostart has been enabled print the comments list immediatly
448 $html .= html_writer
::start_tag('ul', array('id' => 'comment-list-'.$this->cid
, 'class' => 'comment-list comments-loaded'));
449 $html .= html_writer
::tag('li', '', array('class' => 'first'));
450 $html .= $this->print_comments(0, true, false);
451 $html .= html_writer
::end_tag('ul'); // .comment-list
452 $html .= $this->get_pagination(0);
454 $html .= html_writer
::start_tag('ul', array('id' => 'comment-list-'.$this->cid
, 'class' => 'comment-list'));
455 $html .= html_writer
::tag('li', '', array('class' => 'first'));
456 $html .= html_writer
::end_tag('ul'); // .comment-list
457 $html .= html_writer
::tag('div', '', array('id' => 'comment-pagination-'.$this->cid
, 'class' => 'comment-pagination'));
460 if ($this->can_post()) {
461 // print posting textarea
462 $html .= html_writer
::start_tag('div', array('class' => 'comment-area'));
463 $html .= html_writer
::start_tag('div', array('class' => 'db'));
464 $html .= html_writer
::tag('textarea', '', array('name' => 'content', 'rows' => 2, 'cols' => 20, 'id' => 'dlg-content-'.$this->cid
));
465 $html .= html_writer
::end_tag('div'); // .db
467 $html .= html_writer
::start_tag('div', array('class' => 'fd', 'id' => 'comment-action-'.$this->cid
));
468 $html .= html_writer
::link('#', get_string('savecomment'), array('id' => 'comment-action-post-'.$this->cid
));
470 if ($this->displaycancel
) {
471 $html .= html_writer
::tag('span', ' | ');
472 $html .= html_writer
::link('#', get_string('cancel'), array('id' => 'comment-action-cancel-'.$this->cid
));
475 $html .= html_writer
::end_tag('div'); // .fd
476 $html .= html_writer
::end_tag('div'); // .comment-area
477 $html .= html_writer
::tag('div', '', array('class' => 'clearer'));
480 $html .= html_writer
::end_tag('div'); // .comment-ctrl
481 $html .= html_writer
::end_tag('div'); // .mdl-left
494 * Return matched comments
499 public function get_comments($page = '') {
500 global $DB, $CFG, $USER, $OUTPUT;
501 if (!$this->can_view()) {
504 if (!is_numeric($page)) {
508 $perpage = (!empty($CFG->commentsperpage
))?
$CFG->commentsperpage
:15;
509 $start = $page * $perpage;
510 $ufields = user_picture
::fields('u');
511 $sql = "SELECT $ufields, c.id AS cid, c.content AS ccontent, c.format AS cformat, c.timecreated AS ctimecreated
513 JOIN {user} u ON u.id = c.userid
514 WHERE c.contextid = :contextid AND c.commentarea = :commentarea AND c.itemid = :itemid
515 ORDER BY c.timecreated DESC";
516 $params['contextid'] = $this->contextid
;
517 $params['commentarea'] = $this->commentarea
;
518 $params['itemid'] = $this->itemid
;
521 $formatoptions = array('overflowdiv' => true);
522 $rs = $DB->get_recordset_sql($sql, $params, $start, $perpage);
523 foreach ($rs as $u) {
526 $c->content
= $u->ccontent
;
527 $c->format
= $u->cformat
;
528 $c->timecreated
= $u->ctimecreated
;
529 $c->strftimeformat
= get_string('strftimerecent', 'langconfig');
530 $url = new moodle_url('/user/view.php', array('id'=>$u->id
, 'course'=>$this->courseid
));
531 $c->profileurl
= $url->out(true);
532 $c->fullname
= fullname($u);
533 $c->time
= userdate($c->timecreated
, $c->strftimeformat
);
534 $c->content
= format_text($c->content
, $c->format
, $formatoptions);
535 $c->avatar
= $OUTPUT->user_picture($u, array('size'=>18));
538 $candelete = $this->can_delete($c->id
);
539 if (($USER->id
== $u->id
) ||
!empty($candelete)) {
546 if (!empty($this->plugintype
)) {
547 // moodle module will filter comments
548 $comments = plugin_callback($this->plugintype
, $this->pluginname
, 'comment', 'display', array($comments, $this->comment_param
), $comments);
555 * Returns the number of comments associated with the details of this object
557 * @global moodle_database $DB
560 public function count() {
562 if ($this->totalcommentcount
=== null) {
563 $this->totalcommentcount
= $DB->count_records('comments', array('itemid' => $this->itemid
, 'commentarea' => $this->commentarea
, 'contextid' => $this->context
->id
));
565 return $this->totalcommentcount
;
569 * Returns HTML to display a pagination bar
571 * @global stdClass $CFG
572 * @global core_renderer $OUTPUT
576 public function get_pagination($page = 0) {
577 global $CFG, $OUTPUT;
578 $count = $this->count();
579 $perpage = (!empty($CFG->commentsperpage
))?
$CFG->commentsperpage
:15;
580 $pages = (int)ceil($count/$perpage);
581 if ($pages == 1 ||
$pages == 0) {
582 return html_writer
::tag('div', '', array('id' => 'comment-pagination-'.$this->cid
, 'class' => 'comment-pagination'));
584 if (!empty(self
::$nonjs)) {
585 // used in non-js interface
586 return $OUTPUT->paging_bar($count, $page, $perpage, $this->get_nojslink(), 'comment_page');
588 // return ajax paging bar
590 $str .= '<div class="comment-paging" id="comment-pagination-'.$this->cid
.'">';
591 for ($p=0; $p<$pages; $p++
) {
597 $str .= '<a href="#" class="'.$class.'" id="comment-page-'.$this->cid
.'-'.$p.'">'.($p+
1).'</a> ';
607 * @global moodle_database $DB
608 * @param string $content
612 public function add($content, $format = FORMAT_MOODLE
) {
613 global $CFG, $DB, $USER, $OUTPUT;
614 if (!$this->can_post()) {
615 throw new comment_exception('nopermissiontocomment');
618 $newcmt = new stdClass
;
619 $newcmt->contextid
= $this->contextid
;
620 $newcmt->commentarea
= $this->commentarea
;
621 $newcmt->itemid
= $this->itemid
;
622 $newcmt->content
= $content;
623 $newcmt->format
= $format;
624 $newcmt->userid
= $USER->id
;
625 $newcmt->timecreated
= $now;
627 // This callback allow module to modify the content of comment, such as filter or replacement
628 plugin_callback($this->plugintype
, $this->pluginname
, 'comment', 'add', array(&$newcmt, $this->comment_param
));
630 $cmt_id = $DB->insert_record('comments', $newcmt);
631 if (!empty($cmt_id)) {
632 $newcmt->id
= $cmt_id;
633 $newcmt->strftimeformat
= get_string('strftimerecent', 'langconfig');
634 $newcmt->fullname
= fullname($USER);
635 $url = new moodle_url('/user/view.php', array('id' => $USER->id
, 'course' => $this->courseid
));
636 $newcmt->profileurl
= $url->out();
637 $newcmt->content
= format_text($newcmt->content
, $format, array('overflowdiv'=>true));
638 $newcmt->avatar
= $OUTPUT->user_picture($USER, array('size'=>16));
640 $commentlist = array($newcmt);
642 if (!empty($this->plugintype
)) {
643 // Call the display callback to allow the plugin to format the newly added comment.
644 $commentlist = plugin_callback($this->plugintype
,
648 array($commentlist, $this->comment_param
),
650 $newcmt = $commentlist[0];
652 $newcmt->time
= userdate($newcmt->timecreated
, $newcmt->strftimeformat
);
654 // Trigger comment created event.
655 $eventclassname = '\\' . $this->component
. '\\event\comment_created';
656 if (class_exists($eventclassname)) {
657 $event = $eventclassname::create(
659 'context' => $this->context
,
660 'objectid' => $newcmt->id
,
662 'itemid' => $this->itemid
670 throw new comment_exception('dbupdatefailed');
675 * delete by context, commentarea and itemid
676 * @param stdClass|array $param {
677 * contextid => int the context in which the comments exist [required]
678 * commentarea => string the comment area [optional]
679 * itemid => int comment itemid [optional]
683 public static function delete_comments($param) {
685 $param = (array)$param;
686 if (empty($param['contextid'])) {
689 $DB->delete_records('comments', $param);
694 * Delete page_comments in whole course, used by course reset
696 * @param stdClass $context course context
698 public static function reset_course_page_comments($context) {
701 $contexts[] = $context->id
;
702 $children = $context->get_child_contexts();
703 foreach ($children as $c) {
704 $contexts[] = $c->id
;
706 list($ids, $params) = $DB->get_in_or_equal($contexts);
707 $DB->delete_records_select('comments', "commentarea='page_comments' AND contextid $ids", $params);
713 * @param int $commentid
716 public function delete($commentid) {
718 $candelete = has_capability('moodle/comment:delete', $this->context
);
719 if (!$comment = $DB->get_record('comments', array('id'=>$commentid))) {
720 throw new comment_exception('dbupdatefailed');
722 if (!($USER->id
== $comment->userid ||
!empty($candelete))) {
723 throw new comment_exception('nopermissiontocomment');
725 $DB->delete_records('comments', array('id'=>$commentid));
726 // Trigger comment delete event.
727 $eventclassname = '\\' . $this->component
. '\\event\comment_deleted';
728 if (class_exists($eventclassname)) {
729 $event = $eventclassname::create(
731 'context' => $this->context
,
732 'objectid' => $commentid,
734 'itemid' => $this->itemid
737 $event->add_record_snapshot('comments', $comment);
747 * @param bool $return return comments list string or print it out
748 * @param bool $nonjs print nonjs comments list or not?
749 * @return string|void
751 public function print_comments($page = 0, $return = true, $nonjs = true) {
752 global $DB, $CFG, $PAGE;
754 if (!$this->can_view()) {
759 if (!(self
::$comment_itemid == $this->itemid
&&
760 self
::$comment_context == $this->context
->id
&&
761 self
::$comment_area == $this->commentarea
)) {
764 $comments = $this->get_comments($page);
768 $html .= html_writer
::tag('h3', get_string('comments'));
769 $html .= html_writer
::start_tag('ul', array('id' => 'comment-list-'.$this->cid
, 'class' => 'comment-list'));
771 // Reverse the comments array to display them in the correct direction
772 foreach (array_reverse($comments) as $cmt) {
773 $html .= html_writer
::tag('li', $this->print_comment($cmt, $nonjs), array('id' => 'comment-'.$cmt->id
.'-'.$this->cid
));
776 $html .= html_writer
::end_tag('ul');
777 $html .= $this->get_pagination($page);
779 if ($nonjs && $this->can_post()) {
780 // Form to add comments
781 $html .= html_writer
::start_tag('form', array('method' => 'post', 'action' => new moodle_url('/comment/comment_post.php')));
782 // Comment parameters
783 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'contextid', 'value' => $this->contextid
));
784 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'add'));
785 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'area', 'value' => $this->commentarea
));
786 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'component', 'value' => $this->component
));
787 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'itemid', 'value' => $this->itemid
));
788 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'courseid', 'value' => $this->courseid
));
789 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
790 $html .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'returnurl', 'value' => $PAGE->url
));
791 // Textarea for the actual comment
792 $html .= html_writer
::tag('textarea', '', array('name' => 'content', 'rows' => 2));
793 // Submit button to add the comment
794 $html .= html_writer
::empty_tag('input', array('type' => 'submit', 'value' => get_string('submit')));
795 $html .= html_writer
::end_tag('form');
805 * Returns an array containing comments in HTML format.
807 * @global core_renderer $OUTPUT
808 * @param stdClass $cmt {
809 * id => int comment id
810 * content => string comment content
811 * format => int comment text format
812 * timecreated => int comment's timecreated
813 * profileurl => string link to user profile
814 * fullname => comment author's full name
815 * avatar => string user's avatar
816 * delete => boolean does user have permission to delete comment?
821 public function print_comment($cmt, $nonjs = true) {
824 $replacements = array();
826 if (!empty($cmt->delete
) && empty($nonjs)) {
827 $deletelink = html_writer
::start_tag('div', array('class'=>'comment-delete'));
828 $deletelink .= html_writer
::start_tag('a', array('href' => '#', 'id' => 'comment-delete-'.$this->cid
.'-'.$cmt->id
));
829 $deletelink .= $OUTPUT->pix_icon('t/delete', get_string('delete'));
830 $deletelink .= html_writer
::end_tag('a');
831 $deletelink .= html_writer
::end_tag('div');
832 $cmt->content
= $deletelink . $cmt->content
;
834 $patterns[] = '___picture___';
835 $patterns[] = '___name___';
836 $patterns[] = '___content___';
837 $patterns[] = '___time___';
838 $replacements[] = $cmt->avatar
;
839 $replacements[] = html_writer
::link($cmt->profileurl
, $cmt->fullname
);
840 $replacements[] = $cmt->content
;
841 $replacements[] = $cmt->time
;
843 // use html template to format a single comment.
844 return str_replace($patterns, $replacements, $this->template
);
848 * Revoke validate callbacks
850 * @param stdClass $params addtionall parameters need to add to callbacks
852 protected function validate($params=array()) {
853 foreach ($params as $key=>$value) {
854 $this->comment_param
->$key = $value;
856 $validation = plugin_callback($this->plugintype
, $this->pluginname
, 'comment', 'validate', array($this->comment_param
), false);
858 throw new comment_exception('invalidcommentparam');
863 * Returns true if the user is able to view comments
866 public function can_view() {
868 return !empty($this->viewcap
);
872 * Returns true if the user can add comments against this comment description
875 public function can_post() {
877 return isloggedin() && !empty($this->postcap
);
881 * Returns true if the user can delete this comment
882 * @param int $commentid
885 public function can_delete($commentid) {
886 $this->validate(array('commentid'=>$commentid));
887 return has_capability('moodle/comment:delete', $this->context
);
891 * Returns the component associated with the comment
894 public function get_compontent() {
895 return $this->component
;
899 * Returns the context associated with the comment
902 public function get_context() {
903 return $this->context
;
907 * Returns the course id associated with the comment
910 public function get_courseid() {
911 return $this->courseid
;
915 * Returns the course module associated with the comment
919 public function get_cm() {
924 * Returns the item id associated with the comment
928 public function get_itemid() {
929 return $this->itemid
;
933 * Returns the comment area associated with the commentarea
937 public function get_commentarea() {
938 return $this->commentarea
;
943 * Comment exception class
946 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
947 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
949 class comment_exception
extends moodle_exception
{