-
Notifications
You must be signed in to change notification settings - Fork 11
/
Ajax.php
407 lines (353 loc) · 12 KB
/
Ajax.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
<?php
/**
* This file provides the handling for some of the AJAX operations, namely the very generic ones fired through action=ajax.
*
* Wedge (http://wedge.org)
* Copyright © 2010 René-Gilles Deberdt, wedge.org
* Portions are © 2011 Simple Machines.
* License: http://wedge.org/license/
*/
if (!defined('WEDGE'))
die('Hacking attempt...');
/**
* This function handles the initial interaction from action=ajax, loading the template then directing process to the appropriate handler.
*
* @see GetJumpTo()
* @see ListMessageIcons()
*/
function Ajax()
{
$sub_actions = array(
'jumpto' => array(
'function' => 'GetJumpTo',
),
'opt' => array(
'function' => 'SetOption',
),
'messageicons' => array(
'function' => 'ListMessageIcons',
),
'wysiwyg' => array(
'function' => 'EditorSwitch',
),
'thought' => array(
'function' => 'Thought',
),
);
if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']]))
fatal_lang_error('no_access', false);
$sub_actions[$_REQUEST['sa']]['function']();
}
/**
* Produces the list of boards and categories for the jump-to dropdown.
*
* - Uses the {@link getBoardList()} function in Subs-MessageIndex.php.
* - Only displays boards the user has permissions to see (does not honor ignored boards preferences)
* - The current board (if there is a current board) is indicated, and so will be in the dataset returned via the template.
*/
function GetJumpTo()
{
global $context, $settings;
// Find the boards/cateogories they can see.
// Note: you can set $context['current_category'] if you have too many boards and it kills performance.
loadSource('Subs-MessageIndex');
$boardListOptions = array(
'use_permissions' => true,
'selected_board' => isset($context['current_board']) ? $context['current_board'] : 0,
'current_category' => isset($context['current_category']) ? $context['current_category'] : null, // null to list all categories
);
$url = !empty($settings['pretty_enable_filters']) ? '<URL>?board=' : '';
$jump_to = getBoardList($boardListOptions);
$skip_this = isset($_REQUEST['board']) ? $_REQUEST['board'] : 0;
$json = array();
foreach ($jump_to as $id_cat => $cat)
{
$json[] = array(
'name' => un_htmlspecialchars(strip_tags($cat['name'])),
);
foreach ($cat['boards'] as $bdata)
$json[] = array(
'level' => (int) $bdata['child_level'],
'id' => $bdata['id'] == $skip_this ? 'skip' : ($url ? $url . $bdata['id'] . '.0' : $bdata['id']),
'name' => un_htmlspecialchars(strip_tags($bdata['name'])),
);
}
// This will be returned as JSON, saving bytes and processing time.
return_json($json);
}
/**
* Sets a user option via JavaScript.
*
* - Accessed via ?action=ajax;sa=opt;var=variable;val=value;session_var=sess_id.
* - Does not log access to the Who's Online log.
* - Requires user to be logged in.
*/
function SetOption()
{
global $options;
// Check the session ID.
checkSession('get');
// If no variables are provided, leave the hell out of here.
if (empty($_POST['v']) || !isset($_POST['val']))
exit;
// Sorry, guests can't go any further than this..
if (we::$is_guest || MID == 0)
obExit(false);
// If this is the admin preferences the passed value will just be an element of it.
if ($_POST['v'] == 'admin_preferences')
{
$options['admin_preferences'] = !empty($options['admin_preferences']) ? unserialize($options['admin_preferences']) : array();
// New thingy...
if (isset($_GET['admin_key']) && strlen($_GET['admin_key']) < 5)
$options['admin_preferences'][$_GET['admin_key']] = $_POST['val'];
// Change the value to be something nice,
$_POST['val'] = serialize($options['admin_preferences']);
}
// Update the option.
wesql::insert('replace',
'{db_prefix}themes',
array('id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
array(MID, $_POST['v'], is_array($_POST['val']) ? implode(',', $_POST['val']) : $_POST['val'])
);
cache_put_data('theme_settings:' . MID, null, 60);
// Nothing to output.
exit;
}
/**
* Produces a list of the message icons, used for the AJAX change-icon selector within the topic view.
*
* - Uses the {@link getMessageIcons()} function in Subs-Editor.php to achieve this.
* - Uses the current board (from $board) to ensure that the correct iconset is loaded, as icons can be per-board.
*/
function ListMessageIcons()
{
global $board;
loadSource('Subs-Editor');
$icons = getMessageIcons($board);
$str = '';
foreach ($icons as $icon)
$str .= '
<icon value="' . $icon['value'] . '" url="' . $icon['url'] . '"><![CDATA[' . cleanXml('<img src="' . $icon['url'] . '" alt="' . $icon['value'] . '" title="' . $icon['name'] . '">') . ']]></icon>';
return_xml('<we>', $str, '</we>');
}
// Handles the processing required to switch between WYSIWYG and BBCode-only editing modes.
function EditorSwitch()
{
checkSession('get');
if (!isset($_REQUEST['view']) || !isset($_REQUEST['message']))
fatal_lang_error('no_access', false);
loadSource('Class-Editor');
// Return the right thing for the mode.
if ((int) $_REQUEST['view'])
{
$_REQUEST['message'] = strtr($_REQUEST['message'], array('#wecol#' => ';', '#welt#' => '<', '#wegt#' => '>', '#weamp#' => '&'));
$message = wedit::bbc_to_html($_REQUEST['message']);
}
else
{
$_REQUEST['message'] = un_htmlspecialchars($_REQUEST['message']);
$_REQUEST['message'] = strtr($_REQUEST['message'], array('#wecol#' => ';', '#welt#' => '<', '#wegt#' => '>', '#weamp#' => '&'));
$message = wedit::html_to_bbc($_REQUEST['message']);
}
return_xml('<we><message view="', (int) $_REQUEST['view'], '">', cleanXml(westr::htmlspecialchars($message)), '</message></we>');
}
function Thought()
{
if (isset($_REQUEST['personal']))
ThoughtPersonal();
// !! We need we::$user if we're going to allow the editing of older messages... Don't forget to check for sessions?
if (we::$is_guest)
exit;
// !! Should we use censorText at store time, or display time...? we::$user (Load.php:1696) begs to differ.
$text = isset($_POST['text']) ? westr::htmlspecialchars(trim($_POST['text']), ENT_NOQUOTES) : '';
if (!empty($text))
{
loadSource('Class-Editor');
wedit::preparsecode($text);
}
// Original thought ID (in case of an edit.)
$oid = isset($_POST['oid']) ? (int) $_POST['oid'] : 0;
$pid = !empty($_POST['parent']) ? (int) $_POST['parent'] : 0;
$mid = !empty($_POST['master']) ? (int) $_POST['master'] : 0;
if (isset($_GET['like']))
{
loadSource('Like');
$_REQUEST['msg'] = $oid;
$_GET['thought'] = true;
Like();
return;
}
// If we have a parent, then get the member data for the parent thought.
if ($pid)
{
$request = wesql::query('
SELECT m.id_member, m.real_name
FROM {db_prefix}thoughts AS t
LEFT JOIN {db_prefix}members AS m ON t.id_member = m.id_member
WHERE id_thought = {int:id_parent}
LIMIT 1',
['id_parent' => $pid]
);
list ($parent_id, $parent_name) = wesql::fetch_row($request);
wesql::free_result($request);
}
// Is this a public thought?
$privacy = isset($_POST['privacy']) && preg_match('~^-?\d+$~', $_POST['privacy']) ? (int) $_POST['privacy'] : PRIVACY_DEFAULT;
/*
// Delete thoughts when they're older than 3 years...?
// Commented out because it's only useful if your forum is very busy...
wesql::query('
DELETE FROM {db_prefix}thoughts
WHERE updated < UNIX_TIMESTAMP() - 3 * 365 * 24 * 3600
');
*/
// Are we asking for an existing thought?
if (!empty($_GET['in']))
{
$request = wesql::query('
SELECT thought
FROM {db_prefix}thoughts
WHERE id_thought = {int:original_id}' . (allowedTo('moderate_forum') ? '' : '
AND id_member = {int:id_member}
LIMIT 1'),
[
'id_member' => MID,
'original_id' => $_GET['in'],
]
);
list ($thought) = wesql::fetch_row($request);
wesql::free_result($request);
return_raw(un_htmlspecialchars($thought));
}
// Is it an edit?
if (!empty($oid))
{
$request = wesql::query('
SELECT t.id_thought, t.thought, t.id_member, m.real_name
FROM {db_prefix}thoughts AS t
INNER JOIN {db_prefix}members AS m ON m.id_member = t.id_member
WHERE t.id_thought = {int:original_id}' . (allowedTo('moderate_forum') ? '' : '
AND t.id_member = {int:id_member}'),
[
'id_member' => MID,
'original_id' => $oid,
]
);
list ($last_thought, $last_text, $last_member, $last_name) = wesql::fetch_row($request);
wesql::free_result($request);
}
// Overwrite previous thought if it's just an edit.
if (!empty($last_thought))
{
// Think before you think!
if (empty($text) && empty($_GET['in']))
{
// Okay, so we want to delete it... Allow plugins to have a last peek.
call_hook('thought_delete', array(&$last_thought, &$last_text));
wesql::query('
DELETE FROM {db_prefix}thoughts
WHERE id_thought = {int:id_thought}',
['id_thought' => $last_thought]
);
}
// If it's similar to the earlier version, don't update the time.
else
{
similar_text($last_text, $text, $percent);
$update = $percent >= 90 ? 'updated' : time();
wesql::query('
UPDATE {db_prefix}thoughts
SET updated = {raw:updated}, thought = {string:thought}, privacy = {int:privacy}
WHERE id_thought = {int:id_thought}',
[
'id_thought' => $last_thought,
'privacy' => $privacy,
'updated' => $update,
'thought' => $text
]
);
call_hook('thought_update', array(&$last_thought, &$privacy, &$update, &$text));
}
}
elseif ($text)
{
// Okay, so this is a new thought... Insert it, we'll cache it if it's not a comment.
wesql::query('
INSERT IGNORE INTO {db_prefix}thoughts (id_parent, id_member, id_master, privacy, updated, thought)
VALUES ({int:id_parent}, {int:id_member}, {int:id_master}, {int:privacy}, {int:updated}, {string:thought})', [
'id_parent' => $pid,
'id_member' => MID,
'id_master' => $mid,
'privacy' => $privacy,
'updated' => time(),
'thought' => $text
]
);
$last_thought = wesql::insert_id();
$user_id = $pid ? (empty($last_member) ? MID : $last_member) : 0;
$user_name = empty($last_name) ? we::$user['name'] : $last_name;
call_hook('thought_add', array(&$privacy, &$text, &$pid, &$mid, &$last_thought, &$user_id, &$user_name));
}
return_thoughts();
}
function return_thoughts()
{
global $context;
// Welcome to the world of rule-bending dirty hacks.
// What you're going to see isn't for the faint-hearted...
// We're going to emulate Wedge building a thought page.
list ($type, $ctx, $page) = explode(' ', isset($_POST['cx']) ? $_POST['cx'] : 'invalid 0 0');
if ($type == 'invalid')
obExit(false);
$_REQUEST['start'] = $page;
loadSource(array('Thoughts', 'Subs-Cache'));
loadTemplate('index'); // We need template_mini_menu
wedge_get_skin_options(); // Yay, another rule broken! We need the SKIN_MOBILE status.
// This is basically return_xml, but with a series of echo's in-between...
clean_output();
header('Content-Type: text/html; charset=UTF-8');
$context['footer_js'] = '';
if ($type == 'latest')
{
embedThoughts($ctx);
template_thoughts_table();
}
elseif ($type == 'thread')
{
$_REQUEST['in'] = $ctx;
Thoughts();
template_thoughts_thread();
}
elseif ($type == 'profile')
{
loadLanguage('Profile');
latestThoughts($ctx);
template_thoughts_table();
}
echo '<script>breakLinks();', $context['footer_js'], '</script>'; // Yayz!
obExit(false); // And finally, we skip the actual templating process.
}
function ThoughtPersonal()
{
// !! Also check for sessions..?
if (we::$is_guest || empty($_REQUEST['in']))
exit;
// Get the thought text, and ensure it's from the current member.
$request = wesql::query('
SELECT id_thought, thought
FROM {db_prefix}thoughts
WHERE id_member = {int:member}
AND id_thought = {int:thought}
LIMIT 1',
array(
'member' => MID,
'thought' => $_REQUEST['in'],
)
);
list ($personal_id_thought, $personal_thought) = wesql::fetch_row($request);
wesql::free_result($request);
// Update their user data to use the new valid thought.
if (!empty($personal_id_thought))
updateMemberData(MID, array('personal_text' => parse_bbc_inline($personal_thought, 'thought', array('user' => MID))));
exit;
}