Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • /comment/ -> lib.php (source)

    Differences Between: [Versions 28 and 29] [Versions 28 and 30] [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

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

    Search This Site: