/[drupal]/contributions/sandbox/junyor/comment.module
ViewVC logotype

Contents of /contributions/sandbox/junyor/comment.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Mon Jan 10 18:43:50 2005 UTC (4 years, 10 months ago) by junyor
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-php
- Cleaned up sandbox
- Beginning work on a Blogger import script
1 <?php
2 // $Id: comment.module,v 1.1 2004/05/17 15:28:43 junyor Exp $
3
4 function comment_help($section = "admin/help#comment") {
5 $output = "";
6
7 switch ($section) {
8 case 'admin/help#comment':
9 $output = t("
10 <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>
11 <h3>User control of comment display</h3>
12 <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>
13 <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 <li><strong>Flat</strong> -- Displays the posts in chronological order, in the order in which they are posted.</li>
16 <li><strong>Expanded</strong> -- Displays the title and text for each post.</li>
17 <li><strong>Collapsed</strong> -- Displays only the title for each post.</li>
18 </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 <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>
25 <p>The following permissions can be enabled for anonymous users, authenticated users, or any other user roles that the administrator chooses to define:</p>
26 <ul>
27 <li><strong>Access comments</strong> -- Allows users to view comments.</li>
28 <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>
29 <li><strong>Moderate comments</strong> -- Allows users to rate comment postings (see more on moderation below).</li>
30 <li><strong>Post comments</strong> -- Allows users to post comments into an administrator moderation queue. Administrators then post the comment to the site.</li>
31 <li><strong>Post comments without approval</strong> -- Allows users to directly post comments. This bypasses the administrator moderation queue.</li>
32 </ul>
33
34 <h3>Notification of new comments</h3>
35 <p>Drupal provides specific features to inform site members when new comments have been posted:</p>
36 <ul>
37 <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 <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>
39 <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 <h3>Comment moderation</h3>
43 <p>On sites with active commenting from users, the administrator can turn over comment moderation to the community. </p>
44 <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>
45 <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 <ul>
50 <li>Excellent +3</li>
51 <li>Insightful +2</li>
52 <li>Caught My Attention +1</li>
53 <li>Useful +1</li>
54 <li>Redundant -1</li>
55 <li>Flame -3</li>
56 </ul>
57 <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>
58 <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 <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>
63 <p>NOTE: Comment ratings are calculated by averaging user votes with the initial rating.</p>
64 <h4>Creating comment thresholds</h4>
65 <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 $output = t("Enables user to comment on content (nodes).");
74 break;
75 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 break;
78 case 'admin/comment':
79 $output = t("Comments let users give feedback to content authors. Here you may review/approve/deny recent comments, and configure moderation if desired.");
80 break;
81 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 break;
84 case 'admin/comment/comments/0':
85 $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.");
86 break;
87 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 case 'admin/comment/moderation':
91 $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 break;
93 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 break;
96 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 break;
99 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 break;
102 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 $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'.");
107 break;
108 }
109 return $output;
110 }
111
112 function comment_help_page() {
113 print theme("page", comment_help());
114 }
115
116 function comment_settings() {
117 $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."));
118 $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."));
119 $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."));
120 $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."));
121 $output = form_group(t('Comment viewing options'), $group);
122
123 $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?"));
124 $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."));
125 $output .= form_group(t('Comment posting settings'), $group);
126
127 $result = db_query("SELECT fid, filter FROM {moderation_filters} ");
128 while ($filter = db_fetch_object($result)) {
129 $thresholds[$filter->fid] = ($filter->filter);
130 }
131 if ($thresholds) {
132 $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."));
133 $output .= form_group(t('Comment moderation settings'), $group);
134 }
135
136 return $output;
137 }
138
139 function comment_user($type, $edit, &$user) {
140 switch ($type) {
141 case "edit":
142 // when user tries to edit his own data
143 return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 64, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short()));
144 case "edit":
145 // validate user data editing
146 return array("signature" => $edit["signature"]);
147 }
148 }
149
150 function comment_access($op, $comment) {
151 global $user;
152
153 if ($op == "edit") {
154
155 /*
156 ** Authenticated users can edit their comments as long they have
157 ** not been replied to. This, in order to avoid people changing
158 ** or revising their statements based on the replies their posts
159 ** got. Furthermore, users can't reply to their own comments and
160 ** are encouraged to extend their original comment.
161 */
162
163 return $user->uid && $user->uid == $comment->uid && comment_num_replies($comment->cid) == 0;
164 }
165
166 }
167 function comment_referer_save() {
168 $_SESSION["comment_referer"] = arg(0) ."/". arg(1) ."/". arg(2);
169 }
170
171 /*
172 ** Restores the referer from a persistent variable:
173 */
174
175 function comment_referer_load() {
176 return $_SESSION["comment_referer"];
177 }
178
179 function comment_edit($cid) {
180 global $user;
181
182 $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));
183 $comment = drupal_unpack($comment);
184 if (comment_access("edit", $comment)) {
185 return comment_preview(object2array($comment));
186 }
187 }
188
189 function comment_reply($pid, $nid) {
190
191 $output = "";
192
193 if (user_access("access comments")) {
194
195 /*
196 ** Show comment
197 */
198
199 if ($pid) {
200 $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid));
201 $comment = drupal_unpack($comment);
202 $output .= theme("comment_view", $comment);
203 }
204 else if (user_access("access content")) {
205 $output .= node_view(node_load(array("nid" => $nid)));
206 $pid = 0;
207 }
208
209 /*
210 ** If possible, show reply form
211 */
212
213 if (node_comment_mode($nid) != 2) {
214 $output .= theme("box", t("Reply"), t("This discussion is closed: you can't post new comments."));
215 }
216 else if (user_access("post comments")) {
217 $output .= theme("comment_form", array("pid" => $pid, "nid" => $nid), t("Reply"));
218 }
219 else {
220 $output .= theme("box", t("Reply"), t("You are not authorized to post comments."));
221 }
222 }
223 else {
224 $output .= theme("box", t("Reply"), t("You are not authorized to view comments."));
225 }
226
227 return $output;
228 }
229
230 function comment_preview($edit) {
231 global $user;
232
233 $output = "";
234
235 foreach ($edit as $key => $value) {
236 $comment->$key = $value;
237 }
238
239 /*
240 ** Attach the user and time information:
241 */
242
243 $comment->uid = $user->uid;
244 $comment->name = $user->name;
245 $comment->timestamp = time();
246
247 /*
248 ** Preview the comment:
249 */
250
251 $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1)));
252 $output .= theme("comment_form", $edit, t("Reply"));
253
254 if ($edit["pid"]) {
255 $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"]));
256 $comment = drupal_unpack($comment);
257 $output .= theme("comment_view", $comment);
258 }
259 else {
260 $output .= node_view(node_load(array("nid" => $edit["nid"])));
261 $edit["pid"] = 0;
262 }
263
264 return $output;
265 }
266
267 /*
268 Items in comments database:
269
270 cid = comment id, unique identifier for comments (set by comment_save)
271 pid = Parent ID, ie cid of the comment which is the parent of the current one (used in threading) (0 = no parent) (set by comment_save)
272 nid = node id, the node the comment is posted to
273 uid = user id of commentor (0 = anonymous comment)
274 subject = subject of comment
275 comment = comment body
276 hostname = IP of commentor
277 timestamp = timestamp of comment (Unix format)
278 score = moderation score of comment
279 status = publication status of comment (http://drupal.org/book/view/4739)
280 thread = thread depth (set by comment_save)
281 users = no idea (set by comment_save)
282 */
283
284 /*
285 ** This function should always be called before comment_save().
286 ** The $user object should be available to this function.
287 */
288
289 /* Questions:
290 1) What happens if user_load can't find the user?
291
292 Tasks:
293 1) Initialize variables
294 2) Add watchdog warning stuff
295 3) Shouldn't the error array be output?
296
297 Notes:
298 1) This function validates input and fills in blanks, but doesn't prevent user tampering. This should be done before sending comments to this function.
299 */
300
301 function comment_validate($node, &$error) {
302 global $user;
303 $error = array();
304
305 /*
306 ** Convert the comment to an object if necessary:
307 */
308
309 $comment = array2object($comment);
310
311 /*
312 ** Validate the user. If the user who generated the comment
313 ** isn't the current user, save the current user and load the
314 ** user data for the commentor. This is usually only useful
315 ** when a module like an importer is adding comments.
316 */
317 if ($comment->uid != $user->uid)
318 {
319 $temp_user = $user;
320 user_load(array(uid => $comment->uid));
321 }
322
323 /*
324 ** Validate the comment's body.
325 */
326
327 if ($comment->comment == "") {
328 watchdog("warning", "comment: empty comment submitted.");
329 $error['comment'] = theme('error', t('The comment you submitted is empty.'));
330 }
331
332 /*
333 ** Validate the subject field. If not specified, extract
334 ** one from the comment's body.
335 */
336
337 /* Remove HTML from subjects */
338 $comment->subject = strip_tags($comment->subject);
339
340 /* If empty subject */
341 if ($comment->subject == "") {
342 /* set subject according to body of comment */
343 $body = strip_tags($comment->comment);
344 $arr = explode(" ",$body);
345
346 for($i=0; $i<5; $i++) { $comment->subject .= $arr[$i]." "; }
347 }
348
349 /*
350 ** Validate the timestamp field:
351 */
352
353 if (!$comment->timestamp) {
354 $comment->timestamp = time();
355 }
356
357 /*
358 ** Set status field.
359 */
360
361 $status = user_access("post comments without approval") ? 0 : 1;
362
363 /*
364 ** Check for duplicate comments. Note that we have to use the
365 ** validated/filtered data to perform such check.
366 */
367
368 $duplicate = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND nid = %d AND subject = '%s' AND comment = '%s'", $comment->pid, $comment->nid, $comment->subject, $comment->comment), 0);
369
370 if ($duplicate != 0) {
371 watchdog("warning", "comment: duplicate '". $comment->subject ."'");
372 $error['comment'] = theme('error', t('The comment you submitted is a duplicate.'));
373 }
374
375 // Reset user
376 $user = user_load(array(uid => $temp_user->uid));
377
378 return $comment;
379 }
380
381 function comment_save($comment) {
382 /* if the comment is being updated */
383 if ($comment->cid)
384 {
385 db_query("UPDATE {comments} SET subject = '%s', comment = '%s', status = %d WHERE cid = %d", $edit["subject"], $edit["comment"], $edit["status"], $comment->id);
386
387 /*
388 ** Fire a hook
389 */
390 module_invoke_all("comment", "update", $edit);
391
392 /*
393 ** Add entry to the watchdog log:
394 */
395 watchdog("special", "comment: updated '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
396
397 drupal_set_message(t("the comment has been saved."));
398 }
399
400 /* Otherwise, it's a new comment */
401 else {
402
403 /*
404 ** Add the comment to database:
405 */
406
407 $roles = variable_get("comment_roles", array());
408 $score = $roles[$user->rid] ? $roles[$user->rid] : 0;
409 $users = serialize(array(0 => $score));
410
411 /*
412 ** Here we are building the thread field. See the comment
413 ** in comment_render().
414 */
415
416 if ($edit["pid"] == 0) {
417 /*
418 ** This is a comment with no parent comment (depth 0): we start
419 ** by retrieving the maximum thread level.
420 */
421
422 $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid = %d", $edit["nid"]));
423
424 // Strip the "/" from the end of the thread
425 $max = rtrim($max, "/");
426
427 /*
428 ** Next, we increase this value by one. Note that we can't
429 ** use 1, 2, 3, ... 9, 10, 11 because we order by string and
430 ** 10 would be right after 1. We use 1, 2, 3, ..., 9, 91,
431 ** 92, 93, ... instead. Ugly but fast.
432 */
433
434 $decimals = (string)substr($max, 0, strlen($max) - 1);
435 $units = substr($max, -1, 1);
436 if ($units) {
437 $units++;
438 }
439 else {
440 $units = 1;
441 }
442
443 if ($units == 10) {
444 $units = "90";
445 }
446
447 // Finally build the thread field for this new comment
448 $thread = "$decimals$units/";
449 }
450 else {
451 /*
452 ** This is comment with a parent comment: we increase
453 ** the part of the thread value at the proper depth.
454 */
455
456 // Get the parent comment:
457 $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = '%d'", $edit["pid"]));
458
459 // Strip the "/" from the end of the parent thread:
460 $parent->thread = (string)rtrim((string)$parent->thread, "/");
461
462 // Get the max value in _this_ thread:
463 $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = '%d'", $parent->thread, $edit["nid"]));
464
465 if ($max == "") {
466 // First child of this parent
467 $thread = "$parent->thread.1/";
468 }
469 else {
470 // Strip the "/" at the end of the thread:
471 $max = rtrim($max, "/");
472
473 // We need to get the value at the correct depth:
474 $parts = explode(".", $max);
475 $parent_depth = count(explode(".", $parent->thread));
476 $last = $parts[$parent_depth];
477
478 /*
479 ** Next, we increase this value by one. Note that we can't
480 ** use 1, 2, 3, ... 9, 10, 11 because we order by string and
481 ** 10 would be right after 1. We use 1, 2, 3, ..., 9, 91,
482 ** 92, 93, ... instead. Ugly but fast.
483 */
484
485 $decimals = (string)substr($last, 0, strlen($last) - 1);
486 $units = substr($last, -1, 1);
487 $units++;
488 if ($units == 10) {
489 $units = "90";
490 }
491
492 // Finally build the thread field for this new comment:
493 $thread = "$parent->thread.". $decimals.$units ."/";
494 }
495 }
496
497
498 $edit["cid"] = db_next_id("{comments}_cid");
499
500 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);
501
502 /*
503 ** Tell the other modules a new comment has been submitted:
504 */
505
506 module_invoke_all("comment", "insert", $edit);
507
508 /*
509 ** Add entry to the watchdog log:
510 */
511
512 watchdog("special", "comment: added '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
513 }
514
515 /*
516 ** Clear the cache so an anonymous user can see his comment being
517 ** added.
518 */
519
520 cache_clear_all();
521 }
522
523 /*
524 To do:
525 1) Check the error value of comment_validate()
526 2) Output the error array
527 */
528
529 function comment_post($edit) {
530 global $user;
531 $error = array();
532
533 if (user_access("post comments") && node_comment_mode($edit["nid"]) == 2) {
534 $comment = comment_validate($edit, $error);
535 comment_save($comment);
536 }
537 else {
538 watchdog("error", "comment: unauthorized comment submitted or comment submitted to a closed node '". $edit["subject"] ."'");
539 return array(t("Error"), t("You are not authorized to post comments, or this node doesn't accept new comments."));
540 }
541
542 /*
543 ** Redirect the user to the node he commented on, or explain queue
544 */
545
546 if ($status == 1) {
547 return array(t("Comment queued"), t("Your comment has been queued for moderation by site administrators and will be published after approval."));
548 }
549 }
550
551 function comment_links($comment, $return = 1) {
552 global $user;
553
554 $links = array();
555
556 /*
557 ** If we are viewing just this comment, we link back to the node
558 */
559
560 if ($return) {
561 $links[] = l(t("parent"), comment_referer_load(), NULL, NULL, "comment-$comment->cid");
562 }
563
564 if (node_comment_mode($comment->nid) == 2) {
565 if (user_access("administer comments") && user_access("access administration pages")) {
566 $links[] = l(t("delete comment"), "admin/comment/delete/$comment->cid");
567 $links[] = l(t("edit comment"), "admin/comment/edit/$comment->cid");
568 $links[] = l(t("reply to this comment"), "comment/reply/$comment->nid/$comment->cid");
569 }
570 else if (user_access("post comments")) {
571 if (comment_access("edit", $comment)) {
572 $links[] = l(t("edit your comment"), "comment/edit/$comment->cid");
573 }
574 $links[] = l(t("reply to this comment"), "comment/reply/$comment->nid/$comment->cid");
575 }
576 else {
577 $links[] = theme("comment_post_forbidden");
578 }
579 }
580
581 if ($moderation = theme("comment_moderation_form", $comment)) {
582 $links[] = $moderation;
583 }
584
585 return $links;
586 }
587
588 function comment_render($node, $cid = 0) {
589 global $user;
590
591 $mode = $_GET["mode"];
592 $order = $_GET["order"];
593 $threshold = $_GET["threshold"];
594 $comments_per_page = $_GET["comments_per_page"];
595 $comment_page = $_GET["comment_page"];
596
597 $output = "";
598
599 if (user_access("access comments")) {
600
601 /*
602 ** Save were we come from so we can go back after a reply
603 */
604
605 comment_referer_save();
606
607 /*
608 ** Pre-process variables:
609 */
610
611 $nid = $node->nid;
612 if (empty($nid)) {
613 $nid = 0;
614 }
615
616 if (empty($mode)) {
617 $mode = $user->mode ? $user->mode : ($_SESSION["comment_mode"] ? $_SESSION["comment_mode"] : variable_get("comment_default_mode", 4));
618 }
619
620 if (empty($order)) {
621 $order = $user->sort ? $user->sort : ($_SESSION["comment_sort"] ? $_SESSION["comment_sort"] : variable_get("comment_default_order", 1));
622 }
623
624 if (empty($threshold)) {
625 $threshold = $user->threshold ? $user->threshold : ($_SESSION["comment_threshold"] ? $_SESSION["comment_threshold"] : variable_get("comment_default_threshold", 0));
626 }
627 $threshold_min = db_result(db_query("SELECT minimum FROM {moderation_filters} WHERE fid = %d", $threshold));
628
629 if (empty($comments_per_page)) {
630 $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"));
631 }
632
633 $output .= "<a id=\"comment\"></a>\n";
634
635
636 if ($cid) {
637
638 /*
639 ** Single comment view
640 */
641
642 $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
643 $output .= form_hidden("nid", $nid);
644
645 $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, 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.picture, u.data, c.score, c.users", $cid);
646
647 if ($comment = db_fetch_object($result)) {
648 $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1)));
649 }
650
651 if ((comment_user_can_moderate($node)) && $user->uid != $comment->uid && !(comment_already_moderated($user->uid, $comment->users))) {
652 $output .= "<div style=\"text-align: center;\">". form_submit(t("Moderate comment")) ."</div><br />";
653 }
654 $output .= "</div></form>";
655 }
656 else {
657
658 /*
659 ** Multiple comments view
660 */
661
662 $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, 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";
663
664 $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread";
665
666 /*
667 ** We want to use the standard pager, but threads would need every
668 ** comment to build the thread structure, so we need to store some
669 ** extra info.
670 **
671 ** We use a "thread" field to store this extra info. The basic idea
672 ** is to store a value and to order by that value. The "thread" field
673 ** keeps this data in a way which is easy to update and convenient
674 ** to use.
675 **
676 ** A "thread" value starts at "1". If we add a child (A) to this
677 ** comment, we assign it a "thread" = "1.1". A child of (A) will have
678 ** "1.1.1". Next brother of (A) will get "1.2". Next brother of the
679 ** parent of (A) will get "2" and so on.
680 **
681 ** First of all note that the thread field stores the depth of the
682 ** comment: depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
683 **
684 ** Now to get the ordering right, consider this example:
685 **
686 ** 1
687 ** 1.1
688 ** 1.1.1
689 ** 1.2
690 ** 2
691 **
692 ** If we "ORDER BY thread ASC" we get the above result, and this is
693 ** the natural order sorted by time. However, if we "ORDER BY thread
694 ** DESC" we get:
695 **
696 ** 2
697 ** 1.2
698 ** 1.1.1
699 ** 1.1
700 ** 1
701 **
702 ** Clearly, this is not a natural way to see a thread, and users
703 ** will get confused. The natural order to show a thread by time
704 ** desc would be:
705 **
706 ** 2
707 ** 1
708 ** 1.2
709 ** 1.1
710 ** 1.1.1
711 **
712 ** which is what we already did before the standard pager patch. To
713 ** achieve this we simply add a "/" at the end of each "thread" value.
714 ** This way out thread fields will look like depicted below:
715 **
716 ** 1/
717 ** 1.1/
718 ** 1.1.1/
719 ** 1.2/
720 ** 2/
721 **
722 ** we add "/" since this char is, in ASCII, higher than every number,
723 ** so if now we "ORDER BY thread DESC" we get the correct order. Try
724 ** it, it works ;). However this would spoil the "ORDER BY thread ASC"
725 ** Here, we do not need to consider the trailing "/" so we use a
726 ** substring only.
727 */
728
729 if ($order == 1) {
730 if ($mode == 1 || $mode == 2) {
731 $query .= " ORDER BY c.timestamp DESC";
732 }
733 else {
734 $query .= " ORDER BY c.thread DESC";
735 }
736 }
737 else if ($order == 2) {
738 if ($mode == 1 || $mode == 2) {
739 $query .= " ORDER BY c.timestamp";
740 }
741 else {
742
743 /*
744 ** See comment above. Analysis learns that this doesn't cost
745 ** too much. It scales much much better than having the whole
746 ** comment structure.
747 */
748
749 $query .= " ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))";
750 }
751 }
752
753 /*
754 ** Start a form, to use with comment control and moderation.
755 */
756
757 $result = pager_query($query, $comments_per_page, 0, "SELECT COUNT(*) FROM {comments} WHERE nid = '". check_query($nid) ."'");
758 if (db_num_rows($result) && (variable_get("comment_controls", 0) == 0 || variable_get("comment_controls", 0) == 2)) {
759 $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
760 $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
761 $output .= form_hidden("nid", $nid);
762 $output .= "</div></form>";
763 }
764
765 $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
766 $output .= form_hidden("nid", $nid);
767
768 while ($comment = db_fetch_object($result)) {
769 $comment = drupal_unpack($comment);
770 $comment->depth = count(explode(".", $comment->thread)) - 1;
771
772 if ($mode == 1) {
773 $output .= theme("comment_flat_collapsed", $comment, $threshold_min);
774 }
775 else if ($mode == 2) {
776 $output .= theme("comment_flat_expanded", $comment, $threshold_min);
777 }
778 else if ($mode == 3) {
779 $output .= theme("comment_thread_min", $comment, $threshold_min);
780 }
781 else if ($mode == 4) {
782 $output .= theme("comment_thread_max", $comment, $threshold_min);
783 }
784 }
785
786 /*
787 ** Use the standard pager, $pager_total is the number of returned rows,
788 ** is global and defined in pager.inc
789 */
790 if ($pager = theme("pager", NULL, $comments_per_page, 0, array("comments_per_page" => $comments_per_page))) {
791 $output .= $pager;
792 }
793
794 if (db_num_rows($result) && comment_user_can_moderate($node)) {
795 $output .= "<div align=\"center\">". form_submit(t("Moderate comments")) ."</div><br />";
796 }
797
798 $output .= "</div></form>";
799
800 if (db_num_rows($result) && (variable_get("comment_controls", 0) == 1 || variable_get("comment_controls", 0) == 2)) {
801 $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
802 $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
803 $output .= form_hidden("nid", $nid);
804 $output .= "</div></form>";
805 }
806 }
807
808 /*
809 ** If enabled, show new comment form
810 */
811
812 if (user_access("post comments") && node_comment_mode($nid) == 2 && variable_get("comment_form_location", 0)) {
813 $output .= theme("comment_form", array("nid" => $nid), t("Post new comment"));
814 }
815 }
816 return $output;
817 }
818
819 function comment_perm() {
820 return array("access comments", "post comments", "administer comments", "moderate comments", "post comments without approval", "administer moderation");
821 }
822
823 function comment_link($type, $node = 0, $main = 0) {
824 $links = array();
825
826 if ($type == "node" && $node->comment) {
827
828 if ($main) {
829
830 /*
831 ** Main page: display the number of comments that have been posted.
832 */
833
834 if (user_access("access comments")) {
835 $all = comment_num_all($node->nid);
836 $new = comment_num_new($node->nid);
837
838 if ($all) {
839 $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");
840
841 if ($new) {
842 $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");
843 }
844 }
845 else {
846 if ($node->comment == 2) {
847 if (user_access("post comments")) {
848 $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Add a new comment to this page.")));
849 }
850 else {
851 $links[] = theme("comment_post_forbidden");
852 }
853 }
854 }
855 }
856 }
857 else {
858 /*
859 ** Node page: add a "post comment" link if the user is allowed to
860 ** post comments and if this node is not read-only
861 */
862
863 if ($node->comment == 2) {
864 if (user_access("post comments")) {
865 $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Share your thoughts and opinions related to this posting.")), NULL, "comment");
866 }
867 else {
868 $links[] = theme("comment_post_forbidden");
869 }
870 }
871 }
872 }
873
874 if ($type == "comment") {
875 $links = comment_links($node, $main);
876 }
877
878 if ($type == "system") {
879 if (user_access("administer comments")) {
880
881 menu("admin/comment", t("comments"), "comment_admin", 1);
882 menu("admin/comment/comments", t("overview"), "comment_admin", 2);
883 menu("admin/comment/comments/0", t("new/updated"), "comment_admin", 1);
884 menu("admin/comment/comments/1", t("approval queue"), "comment_admin", 2);
885 menu("admin/comment/help", t("help"), "comment_help_page", 9);
886 menu("admin/comment/edit", t("edit comment"), "comment_admin", 0, MENU_HIDE);
887 menu("admin/comment/delete", t("delete comment"), "comment_admin", 0, MENU_HIDE);
888 if (module_exist('search')) {
889 menu("admin/comment/search", t("search"), "comment_admin", 8);
890 }
891
892 // comment settings:
893 if (user_access("administer moderation")) {
894 menu("admin/comment/moderation", t("moderation"), "comment_admin", 3);
895 menu("admin/comment/moderation/votes", t("votes"), "comment_admin");
896 menu("admin/comment/moderation/matrix", t("matrix"), "comment_admin");
897 menu("admin/comment/moderation/filters", t("thresholds"), "comment_admin");
898 menu("admin/comment/moderation/roles", t("initial scores"), "comment_admin", 6);
899 }
900 }
901 menu("comment", t("comments"), "comment_page", 0, MENU_HIDE);
902 }
903
904 return $links;
905 }
906
907 function comment_page() {
908 $op = $_POST["op"];
909 $edit = $_POST["edit"];
910
911 if (empty($op)) {
912 $op = arg(1);
913 }
914
915 switch ($op) {
916 case "edit":
917 print theme("page", comment_edit(check_query(arg(2))), t("Edit comment"));;
918 break;
919 case t("Moderate comments"):
920 case t("Moderate comment"):
921 comment_moderate($edit);
922 drupal_goto(comment_referer_load());
923 break;
924 case "reply":
925 print theme("page", comment_reply(check_query(arg(3)), check_query(arg(2))), t("Add new comment"));
926 break;
927 case t("Preview comment"):
928 print theme("page", comment_preview($edit), t("Preview comment"));
929 break;
930 case t("Post comment"):
931 list($error_title, $error_body) = comment_post($edit);
932 if ($error_body) {
933 print theme("page", $error_body, $error_title);
934 }
935 else {
936 drupal_goto(comment_referer_load());
937 }
938 break;
939 case t("Save settings"):
940 $mode = $_POST["mode"];
941 $order = $_POST["order"];
942 $threshold = $_POST["threshold"];
943 $comments_per_page = $_POST["comments_per_page"];
944
945 comment_save_settings(check_query($mode), check_query($order), check_query($threshold), check_query($comments_per_page));
946 drupal_goto(comment_referer_load());
947 break;
948 }
949 }
950
951 /**
952 *** admin functions
953 **/
954
955 function comment_node_link($node) {
956
957 if (user_access("administer comments")) {
958
959 /*
960 ** Edit comments:
961 */
962
963 $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);
964
965
966 $header = array(t("title"), t("author"), array("data" => t("operations"), "colspan" => 3));
967
968 while ($comment = db_fetch_object($result)) {
969 $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"));
970 }
971
972 if ($rows) {
973 $output = "<h3>". t("Edit comments") ."</h3>";
974 $output .= theme("table", $header, $rows);
975 }
976
977 return $output;
978 }
979 }
980
981 function comment_admin_edit($id) {
982
983 $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);
984 $comment = db_fetch_object($result);
985 $comment = drupal_unpack($comment);
986
987 if ($comment) {
988 $form .= form_textfield(t("Authored by"), "name", $user->name, 20, 64);
989 $form .= form_textfield(t("Authored on"), "timestamp", $edit["timestamp"], 20, 64);
990 $form .= form_textfield(t("Subject"), "subject", $comment->subject, 70, 128);
991 $form .= form_textarea(t("Comment"), "comment", $comment->comment, 70, 15, filter_tips_short());
992 $form .= form_radios(t("Status"), "status", $comment->status, array("published", "not published"));
993 $form .= form_hidden("cid", $id);
994 $form .= form_submit(t("Submit"));
995
996 return form($form);
997 }
998 }
999
1000 function _comment_delete_thread($comment) {
1001 // Delete the comment:
1002 db_query("DELETE FROM {comments} WHERE cid = %d", $comment->cid);
1003 watchdog("special", "comment: deleted '$comment->subject'");
1004
1005 // Delete the comment's replies:
1006 $result = db_query("SELECT cid, subject FROM {comments} WHERE pid = %d", $comment->cid);
1007 while ($comment = db_fetch_object($result)) {
1008 _comment_delete_thread($comment);
1009 }
1010 }
1011
1012 function comment_delete($cid, $confirmed = 0) {
1013 $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));
1014
1015 if ($comment->cid) {
1016 if ($confirmed) {
1017 drupal_set_message(t("the comment and all its replies have been deleted."));
1018
1019 /*
1020 ** Delete the comment and all of its replies:
1021 */
1022
1023 _comment_delete_thread($comment);
1024
1025 /*
1026 ** Clear the cache so an anonymous user can see his comment being
1027 ** added.
1028 */
1029
1030 cache_clear_all();
1031 }
1032 else {
1033 drupal_set_message(t("do you want to delete this comment and all its replies?"));
1034
1035 /*
1036 ** Print a confirmation screen:
1037 */
1038
1039 $output = theme("comment", $comment);
1040 $output .= form_submit(t("Delete comment"));
1041
1042 return form($output);
1043 }
1044 }
1045 else {
1046 drupal_set_message(t("the comment no longer exists."));
1047 }
1048 }
1049
1050 function comment_admin_overview($status = 0) {
1051
1052 $header = array(
1053 array("data" => t("subject"), "field" => "subject"),
1054 array("data" => t("author"), "field" => "u.name"),
1055 array("data" => t("status"), "field" => "status"),
1056 array("data" => t("time"), "field" => "c.timestamp", "sort" => "desc"),
1057 array("data" => t("operations"), "colspan" => 2)
1058 );
1059
1060 $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);
1061 $sql .= tablesort_sql($header);
1062 $result = pager_query($sql, 50);
1063
1064 while ($comment = db_fetch_object($result)) {
1065 $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"));
1066 }
1067
1068 if ($pager = theme("pager", NULL, 50, 0, tablesort_pager())) {
1069 $rows[] = array(array("data" => $pager, "colspan" => 6));
1070 }
1071
1072 return theme("table", $header, $rows);
1073 }
1074
1075 function comment_mod_matrix($edit) {
1076
1077 $output .= "<h3>Moderation vote/value matrix</h3>";
1078
1079 if ($edit) {
1080 db_query("DELETE FROM {moderation_roles} ");
1081 foreach ($edit as $role_id => $votes) {
1082 foreach ($votes as $mid => $value) {
1083 $sql[] = "('$mid', '$role_id', '". ($value ? $value : 0) ."')";
1084 }
1085 }
1086 db_query("INSERT INTO {moderation_roles} (mid, rid, value) VALUES ". implode(", ", $sql));
1087 drupal_set_message(t("the vote values have been saved."));
1088 }
1089
1090 $result = db_query("SELECT r.rid, r.name FROM {role} r, {permission} p WHERE r.rid = p.rid AND p.perm LIKE '%moderate comments%'");
1091 $role_names = array();
1092 while ($role = db_fetch_object($result)) {
1093 $role_names[$role->rid] = $role->name;
1094 }
1095
1096 $result = db_query("SELECT rid, mid, value FROM {moderation_roles} ");
1097 while ($role = db_fetch_object($result)) {
1098 $mod_roles[$role->rid][$role->mid] = $role->value;
1099 }
1100
1101 $header = array_merge(array(t("votes")), array_values($role_names));
1102
1103 $result = db_query("SELECT mid, vote FROM {moderation_votes} ORDER BY weight");
1104 while ($vote = db_fetch_object($result)) {
1105 $row = array($vote->vote);
1106 foreach (array_keys($role_names) as $rid) {
1107 $row[] = array("data" => form_textfield(NULL, "$rid][$vote->mid", $mod_roles[$rid][$vote->mid], 4, 3), "align" => "center");
1108 }
1109 $rows[] = $row;
1110 }
1111 $output .= theme("table", $header, $rows);
1112 $output .= "<br />". form_submit(t("Submit votes"));
1113
1114 return form($output);
1115 }
1116
1117 function comment_mod_roles($edit) {
1118
1119 $output .= "<h3>Initial comment scores</h3>";
1120
1121 if ($edit) {
1122 variable_set("comment_roles", $edit);
1123 drupal_set_message(t("the comment scores have been saved."));
1124 }
1125
1126 $start_values = variable_get("comment_roles", array());
1127
1128