author | Dan |
Mon, 04 Jan 2010 11:09:50 -0500 | |
changeset 1199 | 8d85b52ac9b6 |
parent 1173 | b5b8e7ab0914 |
child 1216 | 4125e19d3b27 |
permissions | -rw-r--r-- |
1 | 1 |
<?php |
2 |
||
3 |
/* |
|
4 |
* Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
1081
745200a9cc2a
Fixed some upgrade bugs; added support for choosing one's own date/time formats; rebrand as 1.1.7
Dan
parents:
1016
diff
changeset
|
5 |
* Copyright (C) 2006-2009 Dan Fuhry |
1 | 6 |
* |
7 |
* This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
8 |
* as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
9 |
* |
|
10 |
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
11 |
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
12 |
*/ |
|
13 |
||
14 |
/** |
|
15 |
* Class that handles comments. Has HTML/Javascript frontend support. |
|
16 |
* @package Enano CMS |
|
17 |
* @subpackage Comment manager |
|
800
9cdfe82c56cd
Major underlying changes to namespace handling. Each namespace is handled by its own class which extends Namespace_Default. Much greater customization/pluggability potential, at the possible expense of some code reusing (though code reusing has been avoided thus far). Also a bit better handling of page passwords [SECURITY].
Dan
parents:
748
diff
changeset
|
18 |
* @license GNU General Public License <http://www.gnu.org/licenses/gpl-2.0.html> |
1 | 19 |
*/ |
20 |
||
21 |
class Comments |
|
22 |
{ |
|
23 |
# |
|
24 |
# VARIABLES |
|
25 |
# |
|
26 |
||
27 |
/** |
|
28 |
* Current list of comments. |
|
29 |
* @var array |
|
30 |
*/ |
|
31 |
||
32 |
var $comments = Array(); |
|
33 |
||
34 |
/** |
|
35 |
* Object to track permissions. |
|
36 |
* @var object |
|
37 |
*/ |
|
38 |
||
39 |
var $perms; |
|
40 |
||
41 |
# |
|
42 |
# METHODS |
|
43 |
# |
|
44 |
||
45 |
/** |
|
46 |
* Constructor. |
|
47 |
* @param string Page ID of the page to load comments for |
|
48 |
* @param string Namespace of the page to load comments for |
|
49 |
*/ |
|
50 |
||
51 |
function __construct($page_id, $namespace) |
|
52 |
{ |
|
53 |
global $db, $session, $paths, $template, $plugins; // Common objects |
|
54 |
||
55 |
// Initialize permissions |
|
322
5f1cd51bf1be
Many changes. Installer with PostgreSQL is broken badly and will be for some time.
Dan
parents:
320
diff
changeset
|
56 |
if ( $page_id == $paths->page_id && $namespace == $paths->namespace ) |
1 | 57 |
$this->perms =& $GLOBALS['session']; |
58 |
else |
|
59 |
$this->perms = $session->fetch_page_acl($page_id, $namespace); |
|
60 |
||
61 |
$this->page_id = $db->escape($page_id); |
|
62 |
$this->namespace = $db->escape($namespace); |
|
63 |
} |
|
64 |
||
65 |
/** |
|
66 |
* Processes a command in JSON format. |
|
1016
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
67 |
* @param mixed Either the JSON-encoded input string, probably something sent from the Javascript/AJAX frontend, or an equivalent array |
1 | 68 |
*/ |
69 |
||
70 |
function process_json($json) |
|
71 |
{ |
|
72 |
global $db, $session, $paths, $template, $plugins; // Common objects |
|
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
73 |
global $lang; |
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
74 |
|
1016
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
75 |
$is_json = !is_array($json); |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
76 |
|
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
77 |
if ( $is_json ) |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
78 |
{ |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
79 |
$data = enano_json_decode($json); |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
80 |
$data = decode_unicode_array($data); |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
81 |
} |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
82 |
else |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
83 |
{ |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
84 |
$data =& $json; |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
85 |
} |
1 | 86 |
if ( !isset($data['mode']) ) |
87 |
{ |
|
86
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
88 |
$ret = Array('mode'=>'error','error'=>'No mode defined!'); |
334
c72b545f1304
More localization work. Resolved major issue with JSON parser not parsing files over ~50KB. Switched JSON parser to the one from the Zend Framework (BSD licensed). Forced to split enano.json into five different files.
Dan
parents:
328
diff
changeset
|
89 |
echo enano_json_encode($ret); |
86
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
90 |
return $ret; |
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
91 |
} |
832
7152ca0a0ce9
Major redesign of rendering pipeline that separates pages saved with MCE from pages saved with the plaintext editor (full description in long commit message)
Dan
parents:
825
diff
changeset
|
92 |
if ( getConfig('enable_comments', '1') == '0' ) |
86
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
93 |
{ |
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
94 |
$ret = Array('mode'=>'error','error'=>'Comments are not enabled on this site.'); |
334
c72b545f1304
More localization work. Resolved major issue with JSON parser not parsing files over ~50KB. Switched JSON parser to the one from the Zend Framework (BSD licensed). Forced to split enano.json into five different files.
Dan
parents:
328
diff
changeset
|
95 |
echo enano_json_encode($ret); |
86
c162ca39db8f
Finished pagination code (was incomplete in previous revision) and added a few hacks for an upcoming theme
Dan
parents:
78
diff
changeset
|
96 |
return $ret; |
1 | 97 |
} |
98 |
$ret = Array(); |
|
99 |
$ret['mode'] = $data['mode']; |
|
1173
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
100 |
if ( isset($data['passback']) ) |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
101 |
$ret['passback'] = $data['passback']; |
1 | 102 |
switch ( $data['mode'] ) |
103 |
{ |
|
104 |
case 'fetch': |
|
105 |
if ( !$template->theme_loaded ) |
|
106 |
$template->load_theme(); |
|
107 |
if ( !isset($data['have_template']) ) |
|
108 |
{ |
|
109 |
$ret['template'] = file_get_contents(ENANO_ROOT . '/themes/' . $template->theme . '/comment.tpl'); |
|
110 |
} |
|
1173
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
111 |
$approve_clause = $this->perms->get_permissions('mod_comments') ? '' : " AND approved = " . COMMENT_APPROVED; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
112 |
// Get totals |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
113 |
$q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$this->page_id' AND namespace = '$this->namespace'{$approve_clause};"); |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
114 |
if ( !$q ) |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
115 |
$db->die_json(); |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
116 |
$counts = array('total' => 0, 'approved' => 0, 'unapproved' => 0, 'spam' => 0); |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
117 |
while ( $row = $db->fetchrow() ) |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
118 |
{ |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
119 |
$counts['total']++; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
120 |
switch($row['approved']): |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
121 |
case COMMENT_APPROVED: $counts['approved']++; break; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
122 |
case COMMENT_UNAPPROVED: $counts['unapproved']++; break; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
123 |
case COMMENT_SPAM: $counts['spam']++; break; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
124 |
endswitch; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
125 |
} |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
126 |
$counts['unapproved'] = $counts['total'] - $counts['approved']; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
127 |
$data['counts'] = $counts; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
128 |
// FIXME, this should be a user preference eventually |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
129 |
$ret['per_page'] = $per_page = getConfig('comments_per_page', 10); |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
130 |
$page = ( !empty($data['pagenum']) ) ? intval($data['pagenum']) : 0; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
131 |
if ( $page > 0 ) |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
132 |
{ |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
133 |
$ret['mode'] = 'refetch'; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
134 |
} |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
135 |
$limit_clause = "LIMIT $per_page OFFSET " . ($page * $per_page); |
621
68f8a9cc0a18
Added Gravatar support! And it's really configurable too.
Dan
parents:
541
diff
changeset
|
136 |
$q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,( c.ip_address IS NOT NULL ) AS have_ip,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type, b.buddy_id IS NOT NULL AS is_buddy, ( b.is_friend IS NOT NULL AND b.is_friend=1 ) AS is_friend FROM '.table_prefix.'comments AS c |
1 | 137 |
LEFT JOIN '.table_prefix.'users AS u |
138 |
ON (u.user_id=c.user_id) |
|
108
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
139 |
LEFT JOIN '.table_prefix.'buddies AS b |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
140 |
ON ( ( b.user_id=' . $session->user_id.' AND b.buddy_user_id=c.user_id ) OR b.user_id IS NULL) |
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
141 |
LEFT JOIN '.table_prefix.'ranks AS r |
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
142 |
ON ( ( u.user_rank = r.rank_id ) ) |
1 | 143 |
WHERE page_id=\'' . $this->page_id . '\' |
108
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
144 |
AND namespace=\'' . $this->namespace . '\' |
1173
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
145 |
' . $approve_clause . ' |
621
68f8a9cc0a18
Added Gravatar support! And it's really configurable too.
Dan
parents:
541
diff
changeset
|
146 |
GROUP BY c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,c.ip_address,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type,b.buddy_id,b.is_friend |
1173
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
147 |
ORDER BY c.time ASC |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
148 |
' . $limit_clause . ';'); |
1 | 149 |
$ret['comments'] = Array(); |
150 |
if (!$q) |
|
151 |
$db->die_json(); |
|
1011
96119a79cf81
Comments: fixed failure to supply $q to fetchrow() in JSON fetcher
Dan
parents:
972
diff
changeset
|
152 |
if ( $row = $db->fetchrow($q) ) |
1 | 153 |
{ |
154 |
do { |
|
155 |
||
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
156 |
if ( !$this->perms->get_permissions('mod_comments') && $row['approved'] != COMMENT_APPROVED ) |
1 | 157 |
continue; |
158 |
||
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
159 |
// Localize the rank |
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
160 |
$row = array_merge($row, $session->get_user_rank(intval($row['user_id']))); |
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
161 |
|
1 | 162 |
// Send the source |
163 |
$row['comment_source'] = $row['comment_data']; |
|
164 |
||
165 |
// Format text |
|
166 |
$row['comment_data'] = RenderMan::render($row['comment_data']); |
|
167 |
||
1163 | 168 |
// Hide it if it's a post from a foe |
108
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
169 |
if ( $row['is_buddy'] == 1 && $row['is_friend'] == 0 ) |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
170 |
{ |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
171 |
$seed = md5(sha1(mt_rand() . microtime())); |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
172 |
$wrapper = ' |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
173 |
<div id="posthide_'.$seed.'" style="display: none;"> |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
174 |
' . $row['comment_data'] . ' |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
175 |
</div> |
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
176 |
<p><span style="opacity: 0.4; filter: alpha(opacity=40);">' . $lang->get('comment_msg_foe_comment_hidden') . '</span> <span style="text-align: right;"><a href="#showpost" onclick="document.getElementById(\'posthide_'.$seed.'\').style.display=\'block\'; this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode); return false;">' . $lang->get('comment_btn_display_foe_comment') . '</a></span></p> |
108
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
177 |
'; |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
178 |
$row['comment_data'] = $wrapper; |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
179 |
} |
1c7f59df9474
Implemented some extra functionality for friends/foes in comments; fixed lack of table_prefix in stats.php line 63
Dan
parents:
86
diff
changeset
|
180 |
|
1 | 181 |
// Format date |
1081
745200a9cc2a
Fixed some upgrade bugs; added support for choosing one's own date/time formats; rebrand as 1.1.7
Dan
parents:
1016
diff
changeset
|
182 |
$row['time'] = enano_date(ED_DATE | ED_TIME, $row['time']); |
1 | 183 |
|
184 |
// Format signature |
|
185 |
$row['signature'] = ( !empty($row['signature']) ) ? RenderMan::render($row['signature']) : ''; |
|
186 |
||
359 | 187 |
// Do we have the IP? |
188 |
$row['have_ip'] = ( $row['have_ip'] == 1 ); |
|
189 |
||
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
190 |
// Avatar URL |
621
68f8a9cc0a18
Added Gravatar support! And it's really configurable too.
Dan
parents:
541
diff
changeset
|
191 |
$row['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']); |
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
192 |
|
1 | 193 |
// Add the comment to the list |
194 |
$ret['comments'][] = $row; |
|
195 |
||
1011
96119a79cf81
Comments: fixed failure to supply $q to fetchrow() in JSON fetcher
Dan
parents:
972
diff
changeset
|
196 |
} while ( $row = $db->fetchrow($q) ); |
1 | 197 |
} |
198 |
$db->free_result(); |
|
1173
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
199 |
$ret['count_appr'] = $counts['approved']; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
200 |
$ret['count_total'] = $counts['total']; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
201 |
$ret['count_visible'] = $this->perms->get_permissions('mod_comments') ? $counts['total'] : $counts['approved']; |
b5b8e7ab0914
Comments (AJAX): Now paginated server side. Fixes issue 2.
Dan
parents:
1163
diff
changeset
|
202 |
$ret['count_unappr'] = $counts['unapproved']; |
1 | 203 |
$ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments'); |
204 |
$ret['auth_post_comments'] = $this->perms->get_permissions('post_comments'); |
|
205 |
$ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments'); |
|
748
e39454295bbb
Added makeSwitchable Dynano method for textareas; enabled support for makeSwitchable in comment runtime
Dan
parents:
685
diff
changeset
|
206 |
$ret['auth_edit_wysiwyg'] = $this->perms->get_permissions('edit_wysiwyg'); |
1 | 207 |
$ret['user_id'] = $session->user_id; |
208 |
$ret['username'] = $session->username; |
|
209 |
$ret['logged_in'] = $session->user_logged_in; |
|
210 |
||
211 |
$ret['user_level'] = Array(); |
|
212 |
$ret['user_level']['guest'] = USER_LEVEL_GUEST; |
|
213 |
$ret['user_level']['member'] = USER_LEVEL_MEMBER; |
|
214 |
$ret['user_level']['mod'] = USER_LEVEL_MOD; |
|
215 |
$ret['user_level']['admin'] = USER_LEVEL_ADMIN; |
|
216 |
||
832
7152ca0a0ce9
Major redesign of rendering pipeline that separates pages saved with MCE from pages saved with the plaintext editor (full description in long commit message)
Dan
parents:
825
diff
changeset
|
217 |
$ret['approval_needed'] = ( getConfig('approve_comments', '0') == '1' ); |
1 | 218 |
$ret['guest_posting'] = getConfig('comments_need_login'); |
219 |
||
220 |
if ( $ret['guest_posting'] == '1' && !$session->user_logged_in ) |
|
221 |
{ |
|
222 |
$session->kill_captcha(); |
|
223 |
$ret['captcha'] = $session->make_captcha(); |
|
224 |
} |
|
225 |
break; |
|
226 |
case 'edit': |
|
227 |
$cid = (string)$data['id']; |
|
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
228 |
if ( !ctype_digit($cid) || intval($cid) < 1 ) |
1 | 229 |
{ |
230 |
echo '{"mode":"error","error":"HACKING ATTEMPT"}'; |
|
231 |
return false; |
|
232 |
} |
|
233 |
$cid = intval($cid); |
|
234 |
$q = $db->sql_query('SELECT c.user_id,c.approved FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';'); |
|
235 |
if(!$q) |
|
236 |
$db->die_json(); |
|
237 |
$row = $db->fetchrow(); |
|
238 |
$uid = intval($row['user_id']); |
|
239 |
$can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) ); |
|
240 |
if(!$can_edit) |
|
241 |
{ |
|
242 |
echo '{"mode":"error","error":"HACKING ATTEMPT"}'; |
|
243 |
return false; |
|
244 |
} |
|
245 |
$data['data'] = str_replace("\r", '', $data['data']); // Windows compatibility |
|
246 |
$text = RenderMan::preprocess_text($data['data'], true, false); |
|
247 |
$text2 = $db->escape($text); |
|
248 |
$subj = $db->escape(htmlspecialchars($data['subj'])); |
|
249 |
$q = $db->sql_query('UPDATE '.table_prefix.'comments SET subject=\'' . $subj . '\',comment_data=\'' . $text2 . '\' WHERE comment_id=' . $cid . ';'); |
|
250 |
if(!$q) |
|
251 |
$db->die_json(); |
|
252 |
$ret = Array( |
|
253 |
'mode' => 'redraw', |
|
254 |
'id' => $data['local_id'], |
|
255 |
'subj' => htmlspecialchars($data['subj']), |
|
256 |
'text' => RenderMan::render($text), |
|
257 |
'src' => $text, |
|
258 |
'approved' => $row['approved'] |
|
259 |
); |
|
260 |
break; |
|
261 |
case 'delete': |
|
262 |
$cid = (string)$data['id']; |
|
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
263 |
if ( !ctype_digit($cid) || intval($cid) < 1 ) |
1 | 264 |
{ |
265 |
echo '{"mode":"error","error":"HACKING ATTEMPT"}'; |
|
266 |
return false; |
|
267 |
} |
|
268 |
$cid = intval($cid); |
|
269 |
$q = $db->sql_query('SELECT c.user_id FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';'); |
|
270 |
if(!$q) |
|
271 |
$db->die_json(); |
|
272 |
$row = $db->fetchrow(); |
|
273 |
$uid = intval($row['user_id']); |
|
274 |
$can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) ); |
|
275 |
if(!$can_edit) |
|
276 |
{ |
|
277 |
echo '{"mode":"error","error":"HACKING ATTEMPT"}'; |
|
278 |
return false; |
|
279 |
} |
|
280 |
$q = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE comment_id='.$cid.';'); |
|
281 |
if(!$q) |
|
282 |
$db->die_json(); |
|
283 |
$ret = Array( |
|
284 |
'mode' => 'annihilate', |
|
285 |
'id' => $data['local_id'] |
|
286 |
); |
|
287 |
break; |
|
288 |
case 'submit': |
|
289 |
||
290 |
// Now for a huge round of security checks... |
|
291 |
||
292 |
$errors = Array(); |
|
293 |
||
294 |
// Authorization |
|
295 |
// Like the rest of the ACL system, this call is a one-stop check for ALL ACL entries. |
|
296 |
if ( !$this->perms->get_permissions('post_comments') ) |
|
74
68469a95658d
Various bugfixes and cleanups, too much to remember... see the diffs for what got changed :-)
Dan
parents:
73
diff
changeset
|
297 |
$errors[] = 'The site security policy prevents your user account from posting comments;'; |
1 | 298 |
|
299 |
// Guest authorization |
|
300 |
if ( getConfig('comments_need_login') == '2' && !$session->user_logged_in ) |
|
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
301 |
$errors[] = $lang->get('comment_err_need_login'); |
1 | 302 |
|
303 |
// CAPTCHA code |
|
304 |
if ( getConfig('comments_need_login') == '1' && !$session->user_logged_in ) |
|
305 |
{ |
|
306 |
$real_code = $session->get_captcha($data['captcha_id']); |
|
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
307 |
if ( strtolower($real_code) !== strtolower($data['captcha_code']) ) |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
308 |
$errors[] = $lang->get('comment_err_captcha_wrong'); |
263
d57af0b0302e
Major improvements in the security of the CAPTCHA system (no SQL injection or anything like that); fixed denied form submission due to _af_acting on form object wrongly switched to true
Dan
parents:
166
diff
changeset
|
309 |
$session->kill_captcha(); |
1 | 310 |
} |
311 |
||
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
312 |
// Spam check |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
313 |
$spam_policy = getConfig('comment_spam_policy', 'moderate'); |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
314 |
$sc_name = ( $session->user_logged_in ) ? $session->username : $data['name']; |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
315 |
$sc_mail = ( $session->user_logged_in ) ? $session->email : false; |
972
437f2505d340
Band-Aided user_homepage undefined index error in comments.
Dan
parents:
832
diff
changeset
|
316 |
$sc_url = ( $session->user_logged_in ) ? @$session->user_extra['user_homepage'] : false; |
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
317 |
$spamcheck = $spam_policy === 'accept' ? true : spamalyze($data['text'], $sc_name, $sc_mail, $sc_url); |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
318 |
if ( !$spamcheck && $spam_policy === 'reject' ) |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
319 |
{ |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
320 |
$errors[] = $lang->get('comment_err_spamcheck_failed_rejected'); |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
321 |
} |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
322 |
|
1 | 323 |
if ( count($errors) > 0 ) |
324 |
{ |
|
325 |
$ret = Array( |
|
326 |
'mode' => 'error', |
|
327 |
'error' => implode("\n", $errors) |
|
328 |
); |
|
329 |
} |
|
330 |
else |
|
331 |
{ |
|
332 |
// We're authorized! |
|
333 |
||
334 |
// Preprocess |
|
335 |
$name = ( $session->user_logged_in ) ? htmlspecialchars($session->username) : htmlspecialchars($data['name']); |
|
336 |
$subj = htmlspecialchars($data['subj']); |
|
337 |
$text = RenderMan::preprocess_text($data['text'], true, false); |
|
338 |
$src = $text; |
|
1085
3343a05e7e5b
SECURITY: Comments: fix poor sanitization of subject on initial submit
Dan
parents:
1081
diff
changeset
|
339 |
$sql_subj = $db->escape($subj); |
1 | 340 |
$sql_text = $db->escape($text); |
341 |
$text = RenderMan::render($text); |
|
832
7152ca0a0ce9
Major redesign of rendering pipeline that separates pages saved with MCE from pages saved with the plaintext editor (full description in long commit message)
Dan
parents:
825
diff
changeset
|
342 |
$appr = ( getConfig('approve_comments', '0') == '1' ) ? COMMENT_UNAPPROVED : COMMENT_APPROVED; |
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
343 |
if ( $appr === COMMENT_APPROVED && $spam_policy === 'moderate' && !$spamcheck ) |
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
344 |
$appr = COMMENT_SPAM; |
1 | 345 |
$time = time(); |
1081
745200a9cc2a
Fixed some upgrade bugs; added support for choosing one's own date/time formats; rebrand as 1.1.7
Dan
parents:
1016
diff
changeset
|
346 |
$date = enano_date(ED_DATE | ED_TIME, $time); |
359 | 347 |
$ip = $_SERVER['REMOTE_ADDR']; |
348 |
if ( !is_valid_ip($ip) ) |
|
349 |
die('Hacking attempt'); |
|
1 | 350 |
|
351 |
// Send it to the database |
|
359 | 352 |
$q = $db->sql_query('INSERT INTO '.table_prefix.'comments(page_id,namespace,name,subject,comment_data,approved, time, user_id, ip_address) VALUES' . "\n " . |
1085
3343a05e7e5b
SECURITY: Comments: fix poor sanitization of subject on initial submit
Dan
parents:
1081
diff
changeset
|
353 |
"('$this->page_id', '$this->namespace', '$name', '$sql_subj', '$sql_text', $appr, $time, {$session->user_id}, '$ip');"); |
1 | 354 |
if(!$q) |
355 |
$db->die_json(); |
|
356 |
||
357 |
// Re-fetch |
|
621
68f8a9cc0a18
Added Gravatar support! And it's really configurable too.
Dan
parents:
541
diff
changeset
|
358 |
$q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type FROM '.table_prefix.'comments AS c |
1 | 359 |
LEFT JOIN '.table_prefix.'users AS u |
360 |
ON (u.user_id=c.user_id) |
|
361 |
WHERE page_id=\'' . $this->page_id . '\' |
|
362 |
AND namespace=\'' . $this->namespace . '\' |
|
363 |
AND time='.$time.' ORDER BY comment_id DESC LIMIT 1;'); |
|
364 |
if(!$q) |
|
365 |
$db->die_json(); |
|
366 |
||
367 |
$row = $db->fetchrow(); |
|
368 |
$db->free_result(); |
|
369 |
$row['time'] = $date; |
|
370 |
$row['comment_data'] = $text; |
|
371 |
$row['comment_source'] = $src; |
|
372 |
$ret = Array( |
|
373 |
'mode' => 'materialize' |
|
374 |
); |
|
375 |
$ret = enano_safe_array_merge($ret, $row); |
|
376 |
||
377 |
$ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments'); |
|
378 |
$ret['auth_post_comments'] = $this->perms->get_permissions('post_comments'); |
|
379 |
$ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments'); |
|
380 |
$ret['user_id'] = $session->user_id; |
|
541
acb7e23b6ffa
Massive commit with various changes. Added user ranks system (no admin interface yet) and ability for users to have custom user titles. Made cron framework accept fractions of hours through floating-point intervals. Modifed ACL editor to use miniPrompt framework for close confirmation box. Made avatar system use a special page as opposed to fetching the files directly for caching reasons.
Dan
parents:
536
diff
changeset
|
381 |
$ret['rank_data'] = $session->get_user_rank($session->user_id); |
1 | 382 |
$ret['username'] = $session->username; |
383 |
$ret['logged_in'] = $session->user_logged_in; |
|
384 |
$ret['signature'] = RenderMan::render($row['signature']); |
|
385 |
||
386 |
$ret['user_level_list'] = Array(); |
|
387 |
$ret['user_level_list']['guest'] = USER_LEVEL_GUEST; |
|
388 |
$ret['user_level_list']['member'] = USER_LEVEL_MEMBER; |
|
389 |
$ret['user_level_list']['mod'] = USER_LEVEL_MOD; |
|
390 |
$ret['user_level_list']['admin'] = USER_LEVEL_ADMIN; |
|
621
68f8a9cc0a18
Added Gravatar support! And it's really configurable too.
Dan
parents:
541
diff
changeset
|
391 |
$ret['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']); |
1 | 392 |
} |
393 |
||
394 |
break; |
|
395 |
case 'approve': |
|
396 |
if ( !$this->perms->get_permissions('mod_comments') ) |
|
397 |
{ |
|
398 |
$ret = Array( |
|
399 |
'mode' => 'error', |
|
400 |
'error' => 'You are not authorized to moderate comments.' |
|
401 |
); |
|
334
c72b545f1304
More localization work. Resolved major issue with JSON parser not parsing files over ~50KB. Switched JSON parser to the one from the Zend Framework (BSD licensed). Forced to split enano.json into five different files.
Dan
parents:
328
diff
changeset
|
402 |
echo enano_json_encode($ret); |
1 | 403 |
return $ret; |
404 |
} |
|
405 |
||
406 |
$cid = (string)$data['id']; |
|
825
9d5c04c1414f
Added (very basic) spam filtering plugin support. Plugins can mark a message as spam by hooking into the spam check API, which is documented in functions.php. No spam checking functionality is built-in.
Dan
parents:
801
diff
changeset
|
407 |
if ( !ctype_digit($cid) || intval($cid) < 1 ) |
1 | 408 |
{ |
409 |
echo '{"mode":"error","error":"HACKING ATTEMPT"}'; |
|
410 |
return false; |
|
411 |
} |
|
412 |
$cid = intval($cid); |
|
413 |
$q = $db->sql_query('SELECT subject,approved FROM '.table_prefix.'comments WHERE comment_id='.$cid.';'); |
|
414 |
if(!$q || $db->numrows() < 1) |
|
415 |
$db->die_json(); |
|
416 |
$row = $db->fetchrow(); |
|
417 |
$db->free_result(); |
|
418 |
$appr = ( $row['approved'] == '1' ) ? '0' : '1'; |
|
419 |
$q = $db->sql_query('UPDATE '.table_prefix."comments SET approved=$appr WHERE comment_id=$cid;"); |
|
420 |
if (!$q) |
|
421 |
$db->die_json(); |
|
422 |
||
423 |
$ret = Array( |
|
424 |
'mode' => 'redraw', |
|
425 |
'approved' => $appr, |
|
426 |
'subj' => $row['subject'], |
|
29
e5484a9e0818
Rewrote change theme dialog; a few minor stability fixes here and there; fixed IE + St Patty background image
Dan
parents:
21
diff
changeset
|
427 |
'id' => $data['local_id'], |
e5484a9e0818
Rewrote change theme dialog; a few minor stability fixes here and there; fixed IE + St Patty background image
Dan
parents:
21
diff
changeset
|
428 |
'approve_updated' => 'yes' |
1 | 429 |
); |
430 |
||
431 |
break; |
|
359 | 432 |
case 'view_ip': |
433 |
if ( !$session->get_permissions('mod_comments') ) |
|
434 |
{ |
|
435 |
return array( |
|
436 |
'mode' => 'error', |
|
437 |
'error' => 'Unauthorized' |
|
438 |
); |
|
439 |
} |
|
440 |
// fetch comment info |
|
441 |
if ( !is_int($data['id']) ) |
|
442 |
{ |
|
443 |
return array( |
|
444 |
'mode' => 'error', |
|
445 |
'error' => 'Unauthorized' |
|
446 |
); |
|
447 |
} |
|
448 |
$id =& $data['id']; |
|
449 |
$q = $db->sql_query('SELECT ip_address, name FROM ' . table_prefix . 'comments WHERE comment_id = ' . $id . ';'); |
|
450 |
if ( !$q || $db->numrows() < 1 ) |
|
451 |
{ |
|
452 |
$db->die_json(); |
|
453 |
} |
|
454 |
list($ip_addr, $name) = $db->fetchrow_num($q); |
|
455 |
$db->free_result(); |
|
456 |
$name = $db->escape($name); |
|
457 |
$username = $db->escape($session->username); |
|
458 |
// log this action |
|
459 |
$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs(time_id, log_type, action, page_text, author, edit_summary) VALUES\n " |
|
460 |
. "( " . time() . ", 'security', 'view_comment_ip', '$name', '$username', '{$_SERVER['REMOTE_ADDR']}' );"); |
|
461 |
if ( !$q ) |
|
462 |
$db->die_json(); |
|
463 |
||
464 |
// send packet |
|
465 |
$ret = array( |
|
466 |
'mode' => 'redraw', |
|
467 |
'ip_addr' => $ip_addr, |
|
468 |
'local_id' => $data['local_id'] |
|
469 |
); |
|
470 |
break; |
|
1 | 471 |
default: |
472 |
$ret = Array( |
|
473 |
'mode' => 'error', |
|
474 |
'error' => $data['mode'] . ' is not a valid request mode' |
|
475 |
); |
|
476 |
break; |
|
477 |
} |
|
1016
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
478 |
if ( $is_json ) |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
479 |
echo enano_json_encode($ret); |
6d32d80b2192
Comments: SECURITY: Fixed IP not recorded in non-JSON submit and a few other non-security issues
Dan
parents:
1011
diff
changeset
|
480 |
|
1 | 481 |
return $ret; |
482 |
} |
|
483 |
||
484 |
} // class Comments |
|
485 |