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