comment.module 67.4 KB
Newer Older
1
<?php
2
// $Id$
3

4
function comment_help($section = "admin/help#comment") {
5 6 7
  $output = "";

  switch ($section) {
8
    case 'admin/help#comment':
9 10 11 12 13
      $output = t("
      <p>When enabled, the Drupal comment module creates a discussion board for each Drupal node. Users can post comments to discuss a forum topic, weblog post, collaborative book page, etc.</p>
      <h3>User control of comment display</h3>
      <p>Attached to each comment board is a control panel for customizing the way that comments are displayed. Users can control the chronological ordering of posts (newest or oldest first) and the number of posts to display on each page. Additional settings include:</p>
      <ul>
14
      <li><strong>Threaded</strong> -- Displays the posts grouped according to conversations and subconversations, much like the subject view of an e-mail client.</li>
15 16 17 18
      <li><strong>Flat</strong> --  Displays the posts in chronological order, in the order in which they are posted.</li>
      <li><strong>Expanded</strong> -- Displays the title and text for each post.</li>
      <li><strong>Collapsed</strong> -- Displays only the title for each post.</li>
      </ul>
19
      <p>When a user chooses <em>save settings</em>, the comments are then redisplayed using the user's new choices. Administrators can set the default settings for the comment control panel, along with other comment defaults, in <a href=\"%comment-config\">administer &raquo; configuration &raquo; modules &raquo; comment</a>.</p>
20
      <p>NOTE: When comment moderation is enabled, users will have another control panel option to control thresholds (see below).</p>
21

22
      <h3>Additional comment configurations</h3>
23
      <p>Comments behave like other user submissions in Drupal. Filters, smileys and HTML that work in nodes will also work with content. To prevent a single user from spamming the web site with too many comments, administrators can set a comment throttle in <a href=\"%site-config\">administer &raquo; configuration</a> under <em>Submission settings</em>.</p>
24 25 26 27 28 29 30 31 32
      <p>Administrators can control access to various comment module functions through <a href=\"%user-permissions\">administer &raquo; accounts &raquo; permissions</a>. Know that in a new Drupal installation, all comment permissions are disabled by default. The choice of which permissions to grant to which roles (groups of users) is left up to the site administrator.</p>
      <p>The following permissions can be enabled for anonymous users, authenticated users, or any other user roles that the administrator chooses to define:</p>
      <ul>
      <li><strong>Access comments</strong> -- Allows users to view comments.</li>
      <li><strong>Administrate comments</strong> -- Allows users complete control over configuring, editing and deleting all comments on the site. Best reserved for <strong>very</strong> trusted users.</li>
      <li><strong>Moderate comments</strong> -- Allows users to rate comment postings (see more on moderation below).</li>
      <li><strong>Post comments</strong> -- Allows users to post comments into an administrator moderation queue. Administrators then post the comment to the site.</li>
      <li><strong>Post comments without approval</strong> -- Allows users to directly post comments. This bypasses the administrator moderation queue.</li>
      </ul>
33

34 35 36 37
      <h3>Notification of new comments</h3>
      <p>Drupal provides specific features to inform site members when new comments have been posted:</p>
      <ul>
      <li>On the home page, Drupal displays the total number of comments attached to each node, and tracks comments read by individual site members. Members which have logged in will see a notice accompanying nodes which contain comments that they have not read.</li>
38 39
      <li>The <em>tracker</em> module, disabled by default, displays all the site's recent posts.  There is a link to the <a href=\"%tracker\">recent posts</a> page in the navigation block.  This page is a useful way to browse new or updated nodes and comments. Content which the user has not yet read is tagged with a red star (this graphic depends on the current theme). Visit the comment board for any node, and Drupal will display a red <em>\"new\"</em> label beside the text of unread comments.</li>
      <li>Some administrators may want to <a href=\"%download-notify\">download</a>, install and configure the notify module. Users can then request that Drupal send them an e-mail when new comments are posted (the notify module requires that cron.php be configured properly).</li>
40
      </ul>
41

42 43 44 45
      <h3>Comment moderation</h3>
      <p>On sites with active commenting from users, the administrator can turn over comment moderation to the community. </p>
      <p>With comment moderation, each comment is automatically assigned an initial rating. As users read comments, they can apply a vote which affects the comment rating. At the same time, users have an additional option in the control panel which allows them to set a threshold for the comments they wish to view. Those comments with ratings lower than the set threshold will not be shown.</p>
      <p>To enable moderation, the administrator must grant <a href=\"%permission\">moderate comments</a> permissions. Then, a number of options in <a href=\"%comment-moderation\">administer &raquo; comments &raquo; moderation</a> must be configured.</p>
46

47
      <h4>Moderation votes</h4>
48
      <p>The first step is to create moderation labels which allow users to rate a comment.  Go to <a href=\"%comment-votes\">administer &raquo; comments &raquo; moderation &raquo; votes</a>. In the <em>vote</em> field, enter the textual labels which users will see when casting their votes. Some examples are</p>
49 50 51 52 53 54 55 56 57 58
      <ul>
      <li>Excellent +3</li>
      <li>Insightful +2</li>
      <li>Caught My Attention +1</li>
      <li>Useful +1</li>
      <li>Redundant -1</li>
      <li>Flame -3</li>
      </ul>
      <p>So that users know how their votes affect the comment, these examples include the vote value as part of the label, although that is optional.</p>
      <p>Using the weight option, you can control the order in which the votes appear to users. Setting the weight heavier (positive numbers) will make the vote label appear at the bottom of the list. Lighter (a negative number) will push it to the top. To encourage positive voting, a useful order might be higher values, positive votes, at the top, with negative votes at the bottom.</p>
59

60
      <h4>Moderator vote/values matrix</h4>
61

62 63 64 65
      <p>Next go to <a href=\"%comment-matrix\">administer &raquo; comments &raquo; moderation &raquo; matrix</a>. Enter the values for the vote labels for each permission role in the vote matrix. The values entered here will be used to create the rating for each comment.</p>
      <p>NOTE: Comment ratings are calculated by averaging user votes with the initial rating.</p>
      <h4>Creating comment thresholds</h4>
      <p>In <a href=\"%comment-thresholds\">administer &raquo; comments &raquo; moderation &raquo; thresholds</a>, you'll have to create some comment thresholds to make the comment rating system useful. When comment moderation is enabled and the thresholds are created, users will find another comment control panel option for selecting their thresholds. They'll use the thresholds you enter here to filter out comments with low ratings. Consequently, you'll probably want to create more than one threshold to give users some flexibility in filtering comments.</p>
66
      <p>When creating the thresholds, note that the <em>Minimum score</em> is asking you for the lowest rating that a comment can have in order to be displayed.</p>
67
      <p>To see a common example of how thresholds work, you might visit <a href=\"%slashdot\">Slashdot</a> and view one of their comment boards associated with a story. You can reset the thresholds in their comment control panel.</p>
68

69
      <h4>Initial comment scores</h4>
70
      <p>Finally, you may want to enter some <em>initial comment scores</em>. In <a href=\"%comment-initial\">administer &raquo; comments &raquo; initial comment scores</a> you can assign a beginning rating for all comments posted by a particular permission role. If you do not assign any initial scores, Drupal will assign a rating of <strong>0</strong> as the default.</p>", array("%comment-config" => url("admin/system/modules/comment"), "%site-config" => url("admin/system"), "%user-permissions" => url("admin/user/permission"), "%tracker" => url("tracker"), "%download-notify" => "http://drupal.org/project/releases", "%permission" => url("admin/user/permission"), "%comment-moderation" => url("admin/comment/moderation"), "%comment-votes" => url("admin/comment/moderation/votes"), "%comment-matrix" => url("admin/comment/moderation/matrix"), "%comment-thresholds" => url("admin/comment/moderation/filters"), "%slashdot" => " http://slashdot.org", "%comment-initial" => url("admin/comment/moderation/roles")));
71
      break;
72
    case 'admin/system/modules#description':
73 74 75
      $output = t("Enables user to comment on content (nodes).");
      break;
    case 'admin/system/modules/comment':
76
      $output = t("Comments can be attached to any node. Below are the settings for comments. The display comes in two types, a \"flat list\" where everything is flush to the left side, and comments come in cronological order, and a \"threaded list\" where comments to other comments are placed immediately below and slightly indented forming an outline. They also come in two styles: \"expanded\", where you see both the title and the contents, and \"collapsed\" where you only see the title. To set the default threshold you first have to set up thresholds in the <a href=\"%threshold\">administer &raquo; comments &raquo; moderation &raquo; thresholds</a> area. Preview comment forces a user to look at their comment by clicking on a \"Preview\" button before they can actually add the comment. If \"New comment form\" is enabled then at the bottom of every comment page there will be a form too add a new comment.", array("%threshold" => url("admin/comment/moderation/filters")));
77 78 79 80 81
      break;
    case 'admin/comment':
      $output = t("Comments let users give feedback to content authors.  Here you may review/approve/deny recent comments, and configure moderation if desired.");
      break;
    case 'admin/comment/comments':
82
      $output = t("Click on <a href=\"%nup\">new or updated comments</a> to see your latest comments, or <a href=\"%queue\">comment approval queue</a> to approve new comments.", array("%nup" => url("admin/comment/comments/0"), "%queue" => url("admin/comment/comments/1")));
83 84 85 86 87
      break;
    case 'admin/comment/comments/0':
      $output = t("Below is a list of the latest comments posted your site. Click on a subject to see the comment, the author's name to edit the author's user information , \"edit comment\" to edit the comment, and \"delete comment\" to remove the comment.");
      break;
    case 'admin/comment/comments/1':
88
      $output = t("Below is a list of the comments posted to your site that need approval. To approve a comment click on <strong>\"edit comment\"</strong> and then change its <strong>moderation status</strong> to Approved.<br />Click on a subject to see the comment, the author's name to edit the author's user information, \"edit comment\" to edit the comment, and \"delete comment\" to remove the comment.");
89
      break;
90 91
    case 'admin/comment/moderation':
      $output = t("If you have a get a lot of comments, you can enable comment moderation. Once moderation is enabled users can vote on a comment based on dropdown menus. <a href=\"%votes\">Votes</a> sets up the names in the dropdown menu, and the order in which they appear, using weights. <a href=\"%matrix\">Matrix</a> sets up the value of each user's vote, and <a href=\"%threshhold\">threshhold</a> sets up the levels at which a comment will be displayed.", array("%votes" => url("admin/comment/moderation/votes"), "%matrix" => url("admin/comment/moderation/matrix"), "%threshhold" => url("admin/comment/moderation/filters")));
92 93
      break;
    case 'admin/comment/moderation/votes':
94
      $output = t("Here is where you setup the names of each type of vote. Weight lets you set the order of the drop down menu. Click <strong>edit</strong> to edit a current vote weight.<br />Notes: <ul><li>you can have more than one type with the same name. The system does not protect you from this.</li><li>To <strong>delete</strong> a name/weight combiniation go to the <strong>edit</strong> area.</li></ul>");
95 96
      break;
    case 'admin/comment/moderation/matrix':
97
      $output = t("Here is where you assign a value to each item in the dropdown menu. This value is added to the vote total, which is then divided by the number of users who have voted and rounded off to the nearest integer.<br />Notes:<ul><li>In order to use comment moderation, every text box on this page should be populated.</li><li>You must assign the <strong>moderate comments</strong> permission to at least one role in order to use this page.</li><li>Every box not filled in will have a value of zero, which will have the effect of <strong>lowering</strong> a comments over all score.</li></ul>");
98 99
      break;
    case 'admin/comment/moderation/filters':
100
      $output = t("<em>Optional</em> Here you can setup the name and minimum \"cut off\" score to help your users hide comments that they don't want too see. These thresholds appear in the Comment Control Panel. Click \"edit\" to edit the values of an already exsisting threashold. To <strong>delete</strong> a threshold click on \"edit\".");
101 102
      break;
    case 'admin/comment/moderation/roles':
103
      $output = t("Here you can setup the <strong>initial</strong> vote value of a comment posted by each user role. This value is used before any other users vote on the comment.<br />Note: Blank entries are valued at zero");
104
      break;
105
    case 'admin/comment/search':
106 107
      $output = t("Enter a simple pattern ( '*' maybe used as a wildcard match) to search for a comment.  For example, one may search for 'br' and Drupal might return 'bread brakers', 'our daily bread' and 'brenda'.");
      break;
108
  }
109
  return $output;
110 111
}

Dries's avatar
Dries committed
112
function comment_help_page() {
113
  print theme("page", comment_help());
Dries's avatar
Dries committed
114 115
}

116
function comment_settings() {
117 118 119 120
  $group  = form_radios(t("Default display mode"), "comment_default_mode", variable_get("comment_default_mode", 4), _comment_get_modes(), t("The default view for comments. Expanded views display the body of the comment. Threaded views keep replies together."));
  $group .= form_radios(t("Default display order"), "comment_default_order", variable_get("comment_default_order", 1), _comment_get_orders(), t("The default sorting for new users and anonymous users while viewing comments. These users may change their view using the comment control panel. For registered users, this change is remembered as a persistent user preference."));
  $group .= form_select(t("Default comments per page"), "comment_default_per_page", variable_get("comment_default_per_page", "50"), _comment_per_page(), t("Default number of comments for each page: more comments are distributed in several pages."));
  $group .= form_radios(t("Comment controls"), "comment_controls", variable_get("comment_controls", 0), array(t("Display above the comments"), t("Display below the comments"), t("Display above and below the comments"), t("Do not display")), t("Position of the comment controls box.  The comment controls let the user change the default display mode and display order of comments."));
Dries's avatar
Dries committed
121
  $output = form_group(t('Comment viewing options'), $group);
122

123 124 125
  $group  = form_radios(t("Preview comment"), "comment_preview", variable_get("comment_preview", 1), array(t("Optional"), t("Required")), t("Must users preview comments before submitting?"));
  $group .= form_radios(t("Location of comment submission form"), "comment_form_location", variable_get("comment_form_location", 0), array(t("Display on separate page"), t("Display below post or comments")), t("The location of the comment submission form."));
  $output .= form_group(t('Comment posting settings'), $group);
126

127
  $result = db_query("SELECT fid, filter FROM {moderation_filters} ");
128 129
  while ($filter = db_fetch_object($result)) {
    $thresholds[$filter->fid] = ($filter->filter);
130
  }
131 132 133 134
  if ($thresholds) {
    $group = form_select(t("Default threshold"), "comment_default_threshold", variable_get("comment_default_threshold", 0), $thresholds, t("Thresholds are values below which comments are hidden. These thresholds are useful for busy sites which want to hide poor comments from most users."));
    $output .= form_group(t('Comment moderation settings'), $group);
  }
135

136
  return $output;
137 138
}

139 140 141 142
function comment_user($type, $edit, &$user) {
  switch ($type) {
    case "view_public":
    case "view_private":
143
      if ($user->signature) {
144
        return form_item(t("Signature"), check_output($user->signature));
145 146
      }
      break;
147 148
    case "edit_form":
      // when user tries to edit his own data
149
      return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short()));
150 151
    case "edit_validate":
      // validate user data editing
152
      return array("signature" => $edit["signature"]);
153 154 155
  }
}

Dries's avatar
Dries committed
156
function comment_access($op, $comment) {
Dries's avatar
Dries committed
157 158
  global $user;

Dries's avatar
Dries committed
159 160 161 162 163 164 165 166 167 168 169
  if ($op == "edit") {

    /*
    ** Authenticated users can edit their comments as long they have
    ** not been replied to.  This, in order to avoid people changing
    ** or revising their statements based on the replies their posts
    ** got. Furthermore, users can't reply to their own comments and
    ** are encouraged to extend their original comment.
    */

    return $user->uid && $user->uid == $comment->uid && comment_num_replies($comment->cid) == 0;
Dries's avatar
Dries committed
170
  }
Dries's avatar
Dries committed
171

Dries's avatar
Dries committed
172
}
173
function comment_referer_save() {
174
  $_SESSION["comment_referer"] = arg(0) ."/". arg(1) ."/". arg(2);
175 176 177 178 179 180 181 182 183
}

/*
** Restores the referer from a persistent variable:
*/

function comment_referer_load() {
  return $_SESSION["comment_referer"];
}
Dries's avatar
Dries committed
184

Dries's avatar
Dries committed
185 186 187
function comment_edit($cid) {
  global $user;

188
  $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2", $cid));
189
  $comment = drupal_unpack($comment);
Dries's avatar
Dries committed
190
  if (comment_access("edit", $comment)) {
191
    return comment_preview(object2array($comment));
Dries's avatar
Dries committed
192 193 194 195
  }
}

function comment_reply($pid, $nid) {
196

197
  $output = "";
198

199
  if (user_access("access comments")) {
200 201 202 203 204

    /*
    ** Show comment
    */

205
    if ($pid) {
206
      $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid));
207
      $comment = drupal_unpack($comment);
208
      $output .= theme("comment_view", $comment);
209
    }
210
    else if (user_access("access content")) {
211
      $output .= node_view(node_load(array("nid" => $nid)));
212 213
      $pid = 0;
    }
Dries's avatar
Dries committed
214

215 216 217 218
    /*
    ** If possible, show reply form
    */

219
    if (node_comment_mode($nid) != 2) {
220
      $output .= theme("box", t("Reply"), t("This discussion is closed: you can't post new comments."));
Kjartan's avatar
Kjartan committed
221
    }
222
    else if (user_access("post comments")) {
223
      $output .= theme("comment_form", array("pid" => $pid, "nid" => $nid), t("Reply"));
224 225
    }
    else {
226
      $output .= theme("box", t("Reply"), t("You are not authorized to post comments."));
227
    }
Kjartan's avatar
Kjartan committed
228 229
  }
  else {
230
    $output .= theme("box", t("Reply"), t("You are not authorized to view comments."));
Dries's avatar
Dries committed
231
  }
232 233

  return $output;
Dries's avatar
Dries committed
234 235 236
}

function comment_preview($edit) {
237
  global $user;
Dries's avatar
Dries committed
238

239 240
  $output = "";

241 242 243 244
  foreach ($edit as $key => $value) {
    $comment->$key = $value;
  }

245
  /*
246
  ** Attach the user and time information:
247 248 249 250 251 252 253 254 255 256
  */

  $comment->uid = $user->uid;
  $comment->name = $user->name;
  $comment->timestamp = time();

  /*
  ** Preview the comment:
  */

257
  $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1)));
258
  $output .= theme("comment_form", $edit, t("Reply"));
Kjartan's avatar
Kjartan committed
259 260

  if ($edit["pid"]) {
261
    $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"]));
262
    $comment = drupal_unpack($comment);
263
    $output .= theme("comment_view", $comment);
Kjartan's avatar
Kjartan committed
264 265
  }
  else {
266
    $output .= node_view(node_load(array("nid" => $edit["nid"])));
Kjartan's avatar
Kjartan committed
267 268
    $edit["pid"] = 0;
  }
269 270

  return $output;
Dries's avatar
Dries committed
271 272 273
}

function comment_post($edit) {
274
  global $user;
Dries's avatar
Dries committed
275

276
  if (user_access("post comments") && node_comment_mode($edit["nid"]) == 2) {
Dries's avatar
Dries committed
277

278 279 280 281 282
    /*
    ** Validate the comment's subject.  If not specified, extract
    ** one from the comment's body.
    */

283
    $edit["subject"] = strip_tags($edit["subject"]);
284

285
    if ($edit["subject"] == "") {
286
      $edit["subject"] = truncate_utf8(strip_tags($edit["comment"]), 29);
287
    }
288 289 290 291 292

    /*
    ** Validate the comment's body.
    */

293 294 295 296
    if ($edit["comment"] == "") {
      return array(t("Empty comment"), t("The comment you submitted is empty."));
    }

297 298 299 300 301
    /*
    ** Check for duplicate comments.  Note that we have to use the
    ** validated/filtered data to perform such check.
    */

302
    $duplicate = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND nid = %d AND subject = '%s' AND comment = '%s'", $edit["pid"], $edit["nid"], $edit["subject"], $edit["comment"]), 0);
Dries's avatar
Dries committed
303 304

    if ($duplicate != 0) {
305
      watchdog("warning", "comment: duplicate '". $edit["subject"] ."'");
306
      return array(t("Duplicate comment"), t("The comment you submitted has already been inserted."));
Dries's avatar
Dries committed
307 308 309
    }
    else {

Dries's avatar
Dries committed
310
      if ($edit["cid"]) {
Dries's avatar
Dries committed
311

Dries's avatar
Dries committed
312 313 314 315 316 317
        /*
        ** Update the comment in the database.  Note that the update
        ** query will fail if the comment isn't owned by the current
        ** user.
        */

318
        db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit["subject"], $edit["comment"], $edit["cid"]);
319 320 321 322 323 324

        /*
        ** Fire a hook
        */

        module_invoke_all("comment", "update", $edit);
Dries's avatar
Dries committed
325 326 327 328 329

        /*
        ** Add entry to the watchdog log:
        */

330
        watchdog("special", "comment: updated '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
Dries's avatar
Dries committed
331 332 333 334 335 336
      }
      else {
        /*
        ** Add the comment to database:
        */

337 338 339 340 341
        $status = user_access("post comments without approval") ? 0 : 1;
        $roles = variable_get("comment_roles", array());
        $score = $roles[$user->rid] ? $roles[$user->rid] : 0;
        $users = serialize(array(0 => $score));

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
        /*
        ** Here we are building the thread field.  See the comment
        ** in comment_render().
        */

        if ($edit["pid"] == 0) {
          /*
          ** This is a comment with no parent comment (depth 0): we start
          ** by retrieving the maximum thread level.
          */

          $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid = %d", $edit["nid"]));

          // Strip the "/" from the end of the thread
          $max = rtrim($max, "/");

          /*
          ** Next, we increase this value by one.  Note that we can't
          ** use 1, 2, 3, ... 9, 10, 11 because we order by string and
          ** 10 would be right after 1.  We use 1, 2, 3, ..., 9, 91,
          ** 92, 93, ... instead.  Ugly but fast.
          */

          $decimals = (string)substr($max, 0, strlen($max) - 1);
          $units = substr($max, -1, 1);
          if ($units) {
            $units++;
          }
          else {
            $units = 1;
          }

          if ($units == 10) {
            $units = "90";
          }

          // Finally build the thread field for this new comment
          $thread = "$decimals$units/";
        }
        else {
          /*
          ** This is comment with a parent comment: we increase
          ** the part of the thread value at the proper depth.
          */

          // Get the parent comment:
          $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = '%d'", $edit["pid"]));

          // Strip the "/" from the end of the parent thread:
          $parent->thread = (string)rtrim((string)$parent->thread, "/");

          // Get the max value in _this_ thread:
          $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = '%d'", $parent->thread, $edit["nid"]));

          if ($max == "") {
            // First child of this parent
            $thread = "$parent->thread.1/";
          }
          else {
            // Strip the "/" at the end of the thread:
            $max = rtrim($max, "/");

            // We need to get the value at the correct depth:
            $parts = explode(".", $max);
406
            $parent_depth = count(explode(".", $parent->thread));
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
            $last = $parts[$parent_depth];

            /*
            ** Next, we increase this value by one.  Note that we can't
            ** use 1, 2, 3, ... 9, 10, 11 because we order by string and
            ** 10 would be right after 1.  We use 1, 2, 3, ..., 9, 91,
            ** 92, 93, ... instead.  Ugly but fast.
            */

            $decimals = (string)substr($last, 0, strlen($last) - 1);
            $units = substr($last, -1, 1);
            $units++;
            if ($units == 10) {
              $units = "90";
            }

            // Finally build the thread field for this new comment:
424
            $thread = "$parent->thread.". $decimals.$units ."/";
425 426 427 428
          }
        }


429
        $edit["cid"] = db_next_id("{comments}_cid");
430

431
        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit["subject"], $edit["comment"], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread);
432 433 434 435 436 437

        /*
        ** Tell the other modules a new comment has been submitted:
        */

        module_invoke_all("comment", "insert", $edit);
Dries's avatar
Dries committed
438 439 440 441

        /*
        ** Add entry to the watchdog log:
        */
442

443
        watchdog("special", "comment: added '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
Dries's avatar
Dries committed
444
      }
445 446

      /*
Dries's avatar
Dries committed
447 448
      ** Clear the cache so an anonymous user can see his comment being
      ** added.
449
      */
Dries's avatar
Dries committed
450

Dries's avatar
Dries committed
451
      cache_clear_all();
Dries's avatar
Dries committed
452 453
    }
  }
454 455 456 457
  else {
    watchdog("error", "comment: unauthorized comment submitted or comment submitted to a closed node '". $edit["subject"] ."'");
    return array(t("Error"), t("You are not authorized to post comments, or this node doesn't accept new comments."));
  }
Dries's avatar
Dries committed
458 459

  /*
460
  ** Redirect the user the node he commented on, or explain queue
Dries's avatar
Dries committed
461 462
  */

463 464
  if ($status == 1) {
    return array(t("Comment queued"), t("Your comment has been queued for moderation by site administrators and will be published after approval."));
Dries's avatar
Dries committed
465 466 467 468
  }
}

function comment_links($comment, $return = 1) {
469
  global $user;
Dries's avatar
Dries committed
470

471
  $links = array();
472

473 474 475 476
  /*
  ** If we are viewing just this comment, we link back to the node
  */

Dries's avatar
Dries committed
477
  if ($return) {
478
    $links[] = l(t("parent"), comment_referer_load(), NULL, NULL, "comment-$comment->cid");
Dries's avatar
Dries committed
479
  }
480

481
  if (node_comment_mode($comment->nid) == 2) {
482 483 484 485 486 487
    if (user_access("administer comments") && user_access("access administration pages")) {
      $links[] = l(t("delete comment"), "admin/comment/delete/$comment->cid");
      $links[] = l(t("edit comment"), "admin/comment/edit/$comment->cid");
      $links[] = l(t("reply to this comment"), "comment/reply/$comment->nid/$comment->cid");
    }
    else if (user_access("post comments")) {
488
      if (comment_access("edit", $comment)) {
489
        $links[] = l(t("edit your comment"), "comment/edit/$comment->cid");
490
      }
Dries's avatar
Dries committed
491
      $links[] = l(t("reply to this comment"), "comment/reply/$comment->nid/$comment->cid");
Dries's avatar
Dries committed
492 493
    }
    else {
494
      $links[] = theme("comment_post_forbidden");
Dries's avatar
Dries committed
495
    }
Dries's avatar
Dries committed
496
  }
497

498
  if ($moderation = theme("comment_moderation_form", $comment)) {
499 500
    $links[] = $moderation;
  }
501

502
  return $links;
Dries's avatar
Dries committed
503 504
}

505
function comment_render($node, $cid = 0) {
506 507 508 509 510 511 512
  global $user;

  $mode = $_GET["mode"];
  $order = $_GET["order"];
  $threshold = $_GET["threshold"];
  $comments_per_page = $_GET["comments_per_page"];
  $comment_page = $_GET["comment_page"];
Dries's avatar
Dries committed
513

514 515
  $output = "";

Dries's avatar
Dries committed
516 517
  if (user_access("access comments")) {

518 519 520 521 522 523
    /*
    ** Save were we come from so we can go back after a reply
    */

    comment_referer_save();

Dries's avatar
Dries committed
524 525 526 527
    /*
    ** Pre-process variables:
    */

528
    $nid = $node->nid;
Dries's avatar
Dries committed
529 530
    if (empty($nid)) {
      $nid = 0;
Dries's avatar
Dries committed
531 532 533
    }

    if (empty($mode)) {
534
      $mode = $user->mode ? $user->mode : ($_SESSION["comment_mode"] ? $_SESSION["comment_mode"] : variable_get("comment_default_mode", 4));
Dries's avatar
Dries committed
535 536 537
    }

    if (empty($order)) {
538
      $order = $user->sort ? $user->sort : ($_SESSION["comment_sort"] ? $_SESSION["comment_sort"] : variable_get("comment_default_order", 1));
Dries's avatar
Dries committed
539 540 541
    }

    if (empty($threshold)) {
542
      $threshold = $user->threshold ? $user->threshold : ($_SESSION["comment_threshold"] ? $_SESSION["comment_threshold"] : variable_get("comment_default_threshold", 0));
Dries's avatar
Dries committed
543
    }
544
    $threshold_min = db_result(db_query("SELECT minimum FROM {moderation_filters} WHERE fid = %d", $threshold));
Dries's avatar
Dries committed
545

546
    if (empty($comments_per_page)) {
547
      $comments_per_page = $user->comments_per_page ? $user->comments_per_page : ($_SESSION["comment_comments_per_page"] ? $_SESSION["comment_comments_per_page"] : variable_get("comment_default_per_page", "50"));
548
    }
549

550
    $output .= "<a id=\"comment\"></a>\n";
Dries's avatar
Dries committed
551 552


Kjartan's avatar
Kjartan committed
553
    if ($cid) {
554 555 556 557 558

      /*
      ** Single comment view
      */

559 560
      $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
      $output .= form_hidden("nid", $nid);
561

562
      $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users", $cid);
563

Dries's avatar
Dries committed
564
      if ($comment = db_fetch_object($result)) {
565
        $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1)));
Dries's avatar
Dries committed
566
      }
567

568
      if ((comment_user_can_moderate($node)) && $user->uid != $comment->uid && !(comment_already_moderated($user->uid, $comment->users))) {
569
        $output .= "<div style=\"text-align: center;\">". form_submit(t("Moderate comment")) ."</div><br />";
570
      }
571
      $output .= "</div></form>";
572
    }
Dries's avatar
Dries committed
573
    else {
574

575 576 577 578
      /*
      ** Multiple comments view
      */

579
      $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0";
580 581

      $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread";
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
      /*
      ** We want to use the standard pager, but threads would need every
      ** comment to build the thread structure, so we need to store some
      ** extra info.
      **
      ** We use a "thread" field to store this extra info. The basic idea
      ** is to store a value and to order by that value. The "thread" field
      ** keeps this data in a way which is easy to update and convenient
      ** to use.
      **
      ** A "thread" value starts at "1". If we add a child (A) to this
      ** comment, we assign it a "thread" = "1.1". A child of (A) will have
      ** "1.1.1". Next brother of (A) will get "1.2". Next brother of the
      ** parent of (A) will get "2" and so on.
      **
      ** First of all note that the thread field stores the depth of the
      ** comment: depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
      **
      ** Now to get the ordering right, consider this example:
      **
      ** 1
      ** 1.1
      ** 1.1.1
      ** 1.2
      ** 2
      **
      ** If we "ORDER BY thread ASC" we get the above result, and this is
      ** the natural order sorted by time.  However, if we "ORDER BY thread
      ** DESC" we get:
      **
      ** 2
      ** 1.2
      ** 1.1.1
      ** 1.1
      ** 1
      **
      ** Clearly, this is not a natural way to see a thread, and users
      ** will get confused. The natural order to show a thread by time
      ** desc would be:
      **
      ** 2
      ** 1
      ** 1.2
      ** 1.1
      ** 1.1.1
      **
      ** which is what we already did before the standard pager patch. To
      ** achieve this we simply add a "/" at the end of each "thread" value.
      ** This way out thread fields will look like depicted below:
      **
      ** 1/
      ** 1.1/
      ** 1.1.1/
      ** 1.2/
      ** 2/
      **
      ** we add "/" since this char is, in ASCII, higher than every number,
      ** so if now we "ORDER BY thread DESC" we get the correct order.  Try
      ** it, it works ;).  However this would spoil the "ORDER BY thread ASC"
      ** Here, we do not need to consider the trailing "/" so we use a
      ** substring only.
      */
645 646

      if ($order == 1) {
647 648 649 650 651 652
        if ($mode == 1 || $mode == 2) {
          $query .= " ORDER BY c.timestamp DESC";
        }
        else {
          $query .= " ORDER BY c.thread DESC";
        }
653
      }
654
      else if ($order == 2) {
655 656 657 658 659 660 661 662 663 664 665 666 667
        if ($mode == 1 || $mode == 2) {
          $query .= " ORDER BY c.timestamp";
        }
        else {

          /*
          ** See comment above.  Analysis learns that this doesn't cost
          ** too much.  It scales much much better than having the whole
          ** comment structure.
          */

          $query .= " ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))";
        }
668 669 670
      }

      /*
671
      ** Start a form, to use with comment control and moderation.
672 673
      */

674
      $result = pager_query($query, $comments_per_page, 0, "SELECT COUNT(*) FROM {comments} WHERE nid = '". check_query($nid) ."'");
675
      if (db_num_rows($result) && (variable_get("comment_controls", 0) == 0 || variable_get("comment_controls", 0) == 2)) {
676 677 678 679
        $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
        $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
        $output .= form_hidden("nid", $nid);
        $output .= "</div></form>";
Dries's avatar
Dries committed
680
      }
681

682 683
      $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
      $output .= form_hidden("nid", $nid);
684

685
      while ($comment = db_fetch_object($result)) {
686
        $comment = drupal_unpack($comment);
687
        $comment->depth = count(explode(".", $comment->thread)) - 1;
688

689
        if ($mode == 1) {
690
          $output .= theme("comment_flat_collapsed", $comment, $threshold_min);
691
        }
692
        else if ($mode == 2) {
693
          $output .= theme("comment_flat_expanded", $comment, $threshold_min);
Dries's avatar
Dries committed
694
        }
695
        else if ($mode == 3) {
696
          $output .= theme("comment_thread_min", $comment, $threshold_min);
697
        }
698
        else if ($mode == 4) {
699
          $output .= theme("comment_thread_max", $comment, $threshold_min);
700 701
        }
      }
702

703 704 705 706
      /*
      ** Use the standard pager, $pager_total is the number of returned rows,
      ** is global and defined in pager.inc
      */
Dries's avatar
Dries committed
707
      if ($pager = theme("pager", NULL, $comments_per_page, 0, array("comments_per_page" => $comments_per_page))) {
708
        $output .= $pager;
709
      }
710

711
      if (db_num_rows($result) && comment_user_can_moderate($node)) {
712
        $output .= "<div align=\"center\">". form_submit(t("Moderate comments")) ."</div><br />";
Dries's avatar
Dries committed
713
      }
714

715
      $output .= "</div></form>";
716

717
      if (db_num_rows($result) && (variable_get("comment_controls", 0) == 1 || variable_get("comment_controls", 0) == 2)) {
718 719 720 721
        $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
        $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
        $output .= form_hidden("nid", $nid);
        $output .= "</div></form>";
722
      }
Dries's avatar
Dries committed
723 724
    }

725 726 727 728
    /*
    ** If enabled, show new comment form
    */

729
    if (user_access("post comments") && node_comment_mode($nid) == 2 && variable_get("comment_form_location", 0)) {
730
      $output .= theme("comment_form", array("nid" => $nid), t("Post new comment"));
731
    }
Dries's avatar
Dries committed
732
  }
733
  return $output;
Dries's avatar
Dries committed
734 735
}

736 737 738
function comment_perm() {
  return array("access comments", "post comments", "administer comments", "moderate comments", "post comments without approval", "administer moderation");
}
Dries's avatar
Dries committed
739

740
function comment_link($type, $node = 0, $main = 0) {
741
  $links = array();
742

743
  if ($type == "node" && $node->comment) {
744 745 746 747 748 749 750 751

    if ($main) {

      /*
      ** Main page: display the number of comments that have been posted.
      */

      if (user_access("access comments")) {
752
        $all = comment_num_all($node->nid);
753
        $new = comment_num_new($node->nid);
754

755
        if ($all) {
756
          $links[] = l(format_plural($all, "1 comment", "%count comments"), "node/view/$node->nid", array("title" => t("Jump to the first comment of this posting.")), NULL, "comment");
757

758
          if ($new) {
759
            $links[] = l(format_plural($new, "1 new comment", "%count new comments"), "node/view/$node->nid", array("title" => t("Jump to the first new comment of this posting.")), NULL, "new");
760 761 762
          }
        }
        else {
763 764 765 766 767 768 769
          if ($node->comment == 2) {
            if (user_access("post comments")) {
              $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Add a new comment to this page.")));
            }
            else {
              $links[] = theme("comment_post_forbidden");
            }
770 771
          }
        }
772 773 774 775 776
      }
    }
    else {
      /*
      ** Node page: add a "post comment" link if the user is allowed to
777
      ** post comments and if this node is not read-only
778 779
      */

780 781
      if ($node->comment == 2) {
        if (user_access("post comments")) {
782
          $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Share your thoughts and opinions related to this posting.")), NULL, "comment");
Kjartan's avatar
Kjartan committed
783 784
        }
        else {
785
          $links[] = theme("comment_post_forbidden");
786
        }
787 788 789 790
      }
    }
  }

791 792 793 794
  if ($type == "comment") {
    $links = comment_links($node, $main);
  }

795 796 797
  if ($type == "system") {
    if (user_access("administer comments")) {

798
      menu("admin/comment", t("comments"), "comment_admin", 1);
799
      menu("admin/comment/comments", t("overview"), "comment_admin", 2);
800 801
      menu("admin/comment/comments/0", t("new/updated"), "comment_admin", 1);
      menu("admin/comment/comments/1", t("approval queue"), "comment_admin", 2);
Dries's avatar
Dries committed
802
      menu("admin/comment/help", t("help"), "comment_help_page", 9);
803
      menu("admin/comment/edit", t("edit comment"), "comment_admin", 0, MENU_HIDE);
804
      menu("admin/comment/delete", t("delete comment"), "comment_admin", 0, MENU_HIDE);
805 806 807
      if (module_exist('search')) {
        menu("admin/comment/search", t("search"), "comment_admin", 8);
      }
808 809 810

      // comment settings:
      if (user_access("administer moderation")) {
811
        menu("admin/comment/moderation", t("moderation"), "comment_admin", 3);
812 813 814 815
        menu("admin/comment/moderation/votes", t("votes"), "comment_admin");
        menu("admin/comment/moderation/matrix", t("matrix"), "comment_admin");
        menu("admin/comment/moderation/filters", t("thresholds"), "comment_admin");
        menu("admin/comment/moderation/roles", t("initial scores"), "comment_admin", 6);
816
      }
817
    }
818
    menu("comment", t("comments"), "comment_page", 0, MENU_HIDE);
819 820
  }

821
  return $links;
822 823
}

Dries's avatar
Dries committed
824
function comment_page() {
825 826
  $op = $_POST["op"];
  $edit = $_POST["edit"];
Dries's avatar
Dries committed
827 828 829 830

  if (empty($op)) {
    $op = arg(1);
  }
Dries's avatar
Dries committed
831 832 833

  switch ($op) {
    case "edit":
834
      print theme("page", comment_edit(check_query(arg(2))), t("Edit comment"));;
Dries's avatar
Dries committed
835
      break;
836 837 838
    case t("Moderate comments"):
    case t("Moderate comment"):
      comment_moderate($edit);
Dries's avatar
Dries committed
839
      drupal_goto(comment_referer_load());
840
      break;
Dries's avatar
Dries committed
841
    case "reply":
842
      print theme("page", comment_reply(check_query(arg(3)), check_query(arg(2))), t("Add new comment"));
Dries's avatar
Dries committed
843 844
      break;
    case t("Preview comment"):
845
      print theme("page", comment_preview($edit), t("Preview comment"));
Dries's avatar
Dries committed
846 847
      break;
    case t("Post comment"):
848 849
      list($error_title, $error_body) = comment_post($edit);
      if ($error_body) {
850
        print theme("page", $error_body, $error_title);
851 852
      }
      else {
Dries's avatar
Dries committed
853
        drupal_goto(comment_referer_load());
854
      }
Dries's avatar
Dries committed
855
      break;
856
    case t("Save settings"):
857 858 859 860 861
      $mode = $_POST["mode"];
      $order = $_POST["order"];
      $threshold = $_POST["threshold"];
      $comments_per_page = $_POST["comments_per_page"];

862
      comment_save_settings(check_query($mode), check_query($order), check_query($threshold), check_query($comments_per_page));
Dries's avatar
Dries committed
863
      drupal_goto(comment_referer_load());
Dries's avatar
Dries committed
864 865 866 867
      break;
  }
}

868 869 870
/**
*** admin functions
**/
Dries's avatar
Dries committed
871

872
function comment_node_link($node) {
873

874
  if (user_access("administer comments")) {
875

876 877 878
    /*
    ** Edit comments:
    */
879

880
    $result = db_query("SELECT c.cid, c.subject, u.uid, u.name FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE nid = %d AND c.status = 0 ORDER BY c.timestamp", $node->nid);
Dries's avatar
Dries committed
881

882 883

    $header = array(t("title"), t("author"), array("data" => t("operations"), "colspan" => 3));
884 885

    while ($comment = db_fetch_object($result)) {
886
      $rows[] = array(l($comment->subject, "node/view/$node->nid", NULL, NULL, "comment-$comment->cid"), format_name($comment), l(t("view comment"), "node/view/$node->nid", NULL, NULL, $comment->cid), l(t("edit comment"), "admin/comment/edit/$comment->cid"), l(t("delete comment"), "admin/comment/delete/$comment->cid"));
887 888
    }

889 890
    if ($rows) {
      $output  = "<h3>". t("Edit comments") ."</h3>";
891
      $output .= theme("table", $header, $rows);
892
    }
893 894

    return $output;
895
  }
896
}
Dries's avatar
Dries committed
897

898 899
function comment_admin_edit($id) {

900
  $result = db_query("SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2", $id);
901
  $comment = db_fetch_object($result);
902
  $comment = drupal_unpack($comment);
903 904 905 906

  if ($comment) {
    $form .= form_item(t("Author"), format_name($comment));
    $form .= form_textfield(t("Subject"), "subject", $comment->subject, 70, 128);
907
    $form .= form_textarea(t("Comment"), "comment", $comment->comment, 70, 15, filter_tips_short());
908
    $form .= form_radios(t("Status"), "status", $comment->status, array("published", "not published"));
909 910 911 912 913
    $form .= form_hidden("cid", $id);
    $form .= form_submit(t("Submit"));

    return form($form);
  }
914 915
}

916 917 918 919 920 921 922 923
function _comment_delete_thread($comment) {
  // Delete the comment:
  db_query("DELETE FROM {comments} WHERE cid = %d", $comment->cid);
  watchdog("special", "comment: deleted '$comment->subject'");

  // Delete the comment's replies:
  $result = db_query("SELECT cid, subject FROM {comments} WHERE pid = %d", $comment->cid);
  while ($comment = db_fetch_object($result)) {
924
    _comment_delete_thread($comment);
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
  }
}

function comment_delete($cid, $confirmed = 0) {
  $comment = db_fetch_object(db_query("SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d", $cid));

  if ($comment->cid) {
    if ($confirmed) {
      drupal_set_message(t("the comment and all its replies have been deleted."));

      /*
      ** Delete the comment and all of its replies:
      */

      _comment_delete_thread($comment);

      /*
      ** Clear the cache so an anonymous user can see his comment being
      ** added.
      */

      cache_clear_all();
    }
    else {
      drupal_set_message(t("do you want to delete this comment and all its replies?"));

      /*
      ** Print a confirmation screen:
      */
Dries's avatar
Dries committed
954

955 956 957 958 959
      $output  = theme("comment", $comment);
      $output .= form_submit(t("Delete comment"));

      return form($output);
    }
Dries's avatar
Dries committed
960 961
  }
  else {
962
    drupal_set_message(t("the comment no longer exists."));
Dries's avatar
Dries committed
963
  }
Dries's avatar
Dries committed
964 965
}

966
function comment_save($id, $edit) {
967
  db_query("UPDATE {comments} SET subject = '%s', comment = '%s', status = %d WHERE cid = %d", $edit["subject"], $edit["comment"], $edit["status"], $id);
968
  watchdog("special", "comment: modified '". $edit["subject"] ."'");
969
  drupal_set_message(t("the comment has been saved."));
970 971
}

Dries's avatar
Dries committed
972
function comment_admin_overview($status = 0) {
973

974
  $header = array(
Dries's avatar
Dries committed
975 976 977
    array("data" => t("subject"), "field" => "subject"),
    array("data" => t("author"), "field" => "u.name"),
    array("data" => t("status"), "field" => "status"),
Dries's avatar
Dries committed
978
    array("data" => t("time"), "field" => "c.timestamp", "sort" => "desc"),
979 980 981
    array("data" => t("operations"), "colspan" => 2)
  );

982
  $sql = "SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.status = ". check_query($status);
983 984
  $sql .= tablesort_sql($header);
  $result = pager_query($sql,  50);
985 986

  while ($comment = db_fetch_object($result)) {
987
    $rows[] = array(l($comment->subject, "node/view/$comment->nid/$comment->cid", array("title" => htmlspecialchars(substr($comment->comment, 0, 128))), NULL, "comment-$comment->cid") ." ". (node_is_new($comment->nid, $comment->timestamp) ? theme("mark") : ""), format_name($comment), ($comment->status == 0 ? t("published") : t("not published")) ."</td><td>". format_date($comment->timestamp, "small") ."</td><td>". l(t("edit comment"), "admin/comment/edit/$comment->cid"), l(t("delete comment"), "admin/comment/delete/$comment->cid"));
988 989
  }

Dries's avatar
Dries committed
990
  if ($pager = theme("pager", NULL, 50, 0, tablesort_pager())) {
991
    $rows[] = array(array("data" => $pager, "colspan" => 6));
992 993
  }

994
  return theme("table", $header, $rows);
995 996 997 998
}

function comment_mod_matrix($edit) {

999
  $output .= "<h3>Moderation vote/value matrix</h3>";
1000

Dries's avatar
Dries committed
1001
  if ($edit) {
1002
    db_query("DELETE FROM {moderation_roles} ");
Dries's avatar
Dries committed
1003
    foreach ($edit as $role_id => $votes) {
1004
      foreach ($votes as $mid => $value) {
1005
        $sql[] = "('$mid', '$role_id', '". ($value ? $value : 0) ."')";
1006 1007
      }
    }
1008
    db_query("INSERT INTO {moderation_roles} (mid, rid, value) VALUES ". implode(", ", $sql));
1009
    drupal_set_message(t("the vote values have been saved."));
1010 1011
  }

1012
  $result = db_query("SELECT r.rid, r.name FROM {role} r, {permission} p WHERE r.rid = p.rid AND p.perm LIKE '%moderate comments%'");
1013 1014 1015 1016 1017
  $role_names = array();
  while ($role = db_fetch_object($result)) {
    $role_names[$role->rid] = $role->name;
  }

1018
  $result = db_query("SELECT rid, mid, value FROM {moderation_roles} ");
1019 1020 1021 1022
  while ($role = db_fetch_object($result)) {
    $mod_roles[$role->rid][$role->mid] = $role->value;
  }

Dries's avatar
Dries committed
1023
  $header = array_merge(array(t("votes")), array_values($role_names));
1024

1025
  $result = db_query("SELECT mid, vote FROM {moderation_votes} ORDER BY weight");
1026
  while ($vote = db_fetch_object($result)) {
Dries's avatar
Dries committed
1027
    $row = array($vote->vote);
1028
    foreach (array_keys($role_names) as $rid) {
Dries's avatar
Dries committed
1029
      $row[] = array("data" => form_textfield(NULL, "$rid][$vote->mid", $mod_roles[$rid][$vote->mid], 4, 3), "align" => "center");
1030
    }
Dries's avatar
Dries committed
1031
    $rows[] = $row;
1032
  }
1033
  $output .= theme("table", $header, $rows);
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
  $output .= "<br />". form_submit(t("Submit votes"));

  return form($output);
}

function comment_mod_roles($edit) {

  $output .= "<h3>Initial comment scores</h3>";

  if ($edit) {
    variable_set("comment_roles", $edit);
1045
    drupal_set_message(t("the comment scores have been saved."));
1046 1047 1048 1049
  }

  $start_values = variable_get("comment_roles", array());

1050
  $result = db_query("SELECT r.rid, r.name FROM {role} r, {permission} p WHERE r.rid = p.rid AND p.perm LIKE '%post comments%'");
1051

Dries's avatar
Dries committed
1052
  $header = array(t("user role"), t("initial score"));
1053 1054

  while ($role = db_fetch_object($result)) {
Dries's avatar
Dries committed
1055
    $rows[] = array($role->name, array("data" => form_textfield(NULL, $role->rid, $start_values[$role->rid], 4, 3), "align" => "center"));
1056 1057
  }

1058
  $output .= theme("table", $header, $rows);
1059 1060 1061 1062 1063 1064
  $output .= "<br />". form_submit(t("Save scores"));

  return form($output);
}

function comment_mod_votes($edit) {
1065
  $op = $_POST["op"];
1066

1067
  $mid = arg(4);
1068 1069

  if ($op == t("Save vote")) {
1070
    db_query("UPDATE {moderation_votes} SET vote = '%s', weight = %d WHERE mid = %d", $edit["vote"], $edit["weight"], $mid);
1071
    $mid = 0;
1072
    drupal_set_message(t("the vote has been saved."));
1073 1074
  }
  else if ($op == t("Delete vote")) {
1075 1076
    db_query("DELETE FROM {moderation_votes} WHERE mid = %d", $mid);
    db_query("DELETE FROM {moderation_roles} WHERE mid = %d", $mid);
1077
    $mid = 0;
1078
    drupal_set_message(t("the vote has been deleted."));
1079 1080
  }
  else if ($op == t("Add new vote")) {
1081
    db_query("INSERT INTO {moderation_votes} (vote, weight) VALUES ('%s', %d)", $edit["vote"], $edit["weight"]);
1082
    $mid = 0;
1083
    drupal_set_message(t("the vote has been added."));
1084 1085
  }

1086
  $output .= "<h3>". t("Moderation votes overview") ."</h3>";
Dries's avatar
Dries committed
1087
  $header = array(t("votes"), t("weight"), t("operations"));
1088

1089
  $result = db_query("SELECT mid, vote, weight FROM {moderation_votes} ORDER BY weight");
1090
  while ($vote = db_fetch_object($result)) {
1091
    $rows[] = array($vote->vote, array("data" => $vote->weight, "align" => "center"), array("data" => l(t("edit"), "admin/comment/moderation/votes/$vote->mid"), "align" => "center"));
1092
  }
1093
  $output .= theme("table", $header, $rows);
1094 1095

  if ($mid) {
1096
    $vote = db_fetch_object(db_query("SELECT vote, weight FROM {moderation_votes} WHERE mid = %d", $mid));
1097 1098
  }

1099
  $output .= "<br /><h3>". (isset($mid) ? "Edit" : "Add new") ." moderation option</h3>";
Dries's avatar
Dries committed
1100
  $form .= form_textfield(t("Vote"), "vote", $vote->vote, 32, 64, t("The name of this vote.  Example: 'off topic', 'excellent', 'sucky'."));
1101
  $form .= form_textfield(t("Weight"), "weight", $vote->weight, 32, 64, t("Used to order votes in the comment control box; heavier sink."));
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
  if ($mid) {
    $form .= form_submit(t("Save vote"));
    $form .= form_submit(t("Delete vote"));
  }
  else {
    $form .= form_submit(t("Add new vote"));
  }

  $output .= form($form);

  return $output;
}

function comment_mod_filters($edit) {
1116
  $op = $_POST["op"];
1117

1118
  $fid = arg(4);
1119

1120
  if ($op == t("Save threshold")) {
1121
    db_query("UPDATE {moderation_filters} SET filter = '%s', minimum = %d WHERE fid = %d", $edit["filter"], $edit["minimum"], $fid);
1122
    $fid = 0;
1123
    drupal_set_message(t("the threshold has been saved."));
1124
  }
1125
  else if ($op == t("Delete threshold")) {
1126
    db_query("DELETE FROM {moderation_filters} WHERE fid = %d", $fid);
1127
    $fid = 0;
1128
    drupal_set_message(t("the threshold has been deleted."));
1129
  }
1130
  else if ($op == t("Add new threshold")) {
1131
    db_query("INSERT INTO {moderation_filters} (filter, minimum) VALUES ('%s', %d)", $edit["filter"], $edit["minimum"]);