· 9 years ago · Jan 27, 2017, 09:46 AM
1<?php
2/* by Tomasz 'Devilshakerz' Mlynski [devilshakerz.com]; Copyright (C) 2014-2015
3 released under Creative Commons BY-NC-SA 4.0 license: http://creativecommons.org/licenses/by-nc-sa/4.0/ */
4
5$plugins->add_hook('global_start', ['dvz_shoutbox', 'global_start']); // cache shoutbox templates
6$plugins->add_hook('global_end', ['dvz_shoutbox', 'global_end']); // catch archive page
7$plugins->add_hook('xmlhttp', ['dvz_shoutbox', 'xmlhttp']); // xmlhttp.php listening
8$plugins->add_hook('index_end', ['dvz_shoutbox', 'load_window']); // load Shoutbox window to {$dvz_shoutbox} variable
9$plugins->add_hook('chat_start', ['dvz_shoutbox', 'load_window']);
10$plugins->add_hook('chat_start', 'dvz_shoutbox_stats');
11$plugins->add_hook('global_start', 'dvz_shoutbox_users_on_sb');
12
13$plugins->add_hook('admin_user_users_merge_commit', ['dvz_shoutbox', 'user_merge']);
14
15$plugins->add_hook('fetch_wol_activity_end', ['dvz_shoutbox', 'activity']); // catch activity
16$plugins->add_hook('build_friendly_wol_location_end', ['dvz_shoutbox', 'activity_translate']); // translate activity
17
18$plugins->add_hook('misc_clearcookies', ['dvz_shoutbox', 'clearcookies']);
19
20function dvz_shoutbox_info()
21{
22 return [
23 'name' => 'DVZ Shoutbox',
24 'description' => 'Lightweight AJAX chat.',
25 'website' => 'http://devilshakerz.com/',
26 'author' => 'Tomasz \'Devilshakerz\' Mlynski',
27 'authorsite' => 'http://devilshakerz.com/',
28 'version' => '2.2.1',
29 'codename' => 'dvz_shoutbox',
30 'compatibility' => '18*',
31 ];
32}
33
34function dvz_shoutbox_install()
35{
36 global $db;
37
38 // table
39 $db->write_query("
40 CREATE TABLE IF NOT EXISTS `" . TABLE_PREFIX . "dvz_shoutbox` (
41 `id` int(11) NOT NULL auto_increment,
42 `uid` int(11) NOT NULL,
43 `text` text NULL,
44 `date` int(11) NOT NULL,
45 `modified` int(11) NULL DEFAULT NULL,
46 `ipaddress` varbinary(16) NOT NULL,
47 PRIMARY KEY (`id`)
48 ) ENGINE=InnoDB " . $db->build_create_table_collation() . "
49 ");
50
51 // example shout
52 $db->insert_query('dvz_shoutbox', [
53 'uid' => 1,
54 'text' => 'DVZ Shoutbox!',
55 'date' => TIME_NOW,
56 'ipaddress' => my_inet_pton('127.0.0.1'),
57 ]);
58
59 // settings
60 $settingGroupId = $db->insert_query('settinggroups', [
61 'name' => 'dvz_shoutbox',
62 'title' => 'DVZ Shoutbox',
63 'description' => 'Settings for DVZ Shoutbox.',
64 ]);
65
66 $settings = [
67 [
68 'name' => 'dvz_sb_num',
69 'title' => 'Shouts to display',
70 'description' => 'Number of shouts to be displayed in the Shoutbox window.',
71 'optionscode' => 'numeric',
72 'value' => '20',
73 ],
74 [
75 'name' => 'dvz_sb_num_archive',
76 'title' => 'Shouts to display on archive',
77 'description' => 'Number of shouts to be displayed per page on Archive view.',
78 'optionscode' => 'numeric',
79 'value' => '20',
80 ],
81 [
82 'name' => 'dvz_sb_reversed',
83 'title' => 'Reversed order',
84 'description' => 'Reverses the shouts display order in the Shoutbox window so that new ones appear on the bottom. You may also want to move the <b>{$panel}</b> variable below the window in the <i>dvz_shoutbox</i> template.',
85 'optionscode' => 'yesno',
86 'value' => '0',
87 ],
88 [
89 'name' => 'dvz_sb_height',
90 'title' => 'Shoutbox height',
91 'description' => 'Height of the Shoutbox window in pixels.',
92 'optionscode' => 'numeric',
93 'value' => '160',
94 ],
95 [
96 'name' => 'dvz_sb_dateformat',
97 'title' => 'Date format',
98 'description' => 'Format of the date displayed. This format uses the PHP\'s <a href="http://php.net/manual/en/function.date.php#refsect1-function.date-parameters">date() function syntax</a>.',
99 'optionscode' => 'text',
100 'value' => 'd M H:i',
101 ],
102 [
103 'name' => 'dvz_sb_maxlength',
104 'title' => 'Maximum message length',
105 'description' => 'Set 0 to disable the limit.',
106 'optionscode' => 'numeric',
107 'value' => '0',
108 ],
109 [
110 'name' => 'dvz_sb_mycode',
111 'title' => 'Parse MyCode',
112 'description' => '',
113 'optionscode' => 'yesno',
114 'value' => '1',
115 ],
116 [
117 'name' => 'dvz_sb_smilies',
118 'title' => 'Parse smilies',
119 'description' => '',
120 'optionscode' => 'yesno',
121 'value' => '1',
122 ],
123 [
124 'name' => 'dvz_sb_interval',
125 'title' => 'Refresh interval',
126 'description' => 'Number of seconds between Shoutbox updates (lower values provide better synchronization but cause higher server load). Set 0 to disable the auto-refreshing feature.',
127 'optionscode' => 'numeric',
128 'value' => '5',
129 ],
130 [
131 'name' => 'dvz_sb_away',
132 'title' => 'Away mode',
133 'description' => 'Number of seconds after last user action (e.g. click) after which shoutbox will be minimized to prevent unnecessary usage of server resources. Set 0 to disable this feature.',
134 'optionscode' => 'numeric',
135 'value' => '600',
136 ],
137 [
138 'name' => 'dvz_sb_antiflood',
139 'title' => 'Anti-flood interval',
140 'description' => 'Forces a minimum number of seconds to last between user\'s shouts (this does not apply to Shoutbox moderators).',
141 'optionscode' => 'numeric',
142 'value' => '5',
143 ],
144 [
145 'name' => 'dvz_sb_sync',
146 'title' => 'Moderation synchronization',
147 'description' => 'Applies moderation actions without refreshing the Shoutbox window page.',
148 'optionscode' => 'onoff',
149 'value' => '1',
150 ],
151 [
152 'name' => 'dvz_sb_lazyload',
153 'title' => 'Lazy load',
154 'description' => 'Start loading data only when the Shoutbox window is actually being displayed on the screen (the page is scrolled to the Shoutbox position).',
155 'optionscode' => 'select
156off=Disabled
157start=Check if on display to start
158always=Always check if on display to refresh',
159 'value' => 'off',
160 ],
161 [
162 'name' => 'dvz_sb_status',
163 'title' => 'Shoutbox default status',
164 'description' => 'Choose whether Shoutbox window should be expanded or collapsed by default.',
165 'optionscode' => 'onoff',
166 'value' => '1',
167 ],
168 [
169 'name' => 'dvz_sb_minposts',
170 'title' => 'Minimum posts required to shout',
171 'description' => 'Set 0 to allow everyone.',
172 'optionscode' => 'numeric',
173 'value' => '0',
174 ],
175 [
176 'name' => 'dvz_sb_groups_view',
177 'title' => 'Group permissions: View',
178 'description' => 'User groups that can view Shoutbox.',
179 'optionscode' => 'groupselect',
180 'value' => '-1',
181 ],
182 [
183 'name' => 'dvz_sb_groups_refresh',
184 'title' => 'Group permissions: Auto-refresh',
185 'description' => 'User groups that shoutbox will be refreshing for.',
186 'optionscode' => 'groupselect',
187 'value' => '-1',
188 ],
189 [
190 'name' => 'dvz_sb_groups_shout',
191 'title' => 'Group permissions: Shout',
192 'description' => 'User groups that can post shouts in Shoutbox (logged in users only).',
193 'optionscode' => 'groupselect',
194 'value' => '-1',
195 ],
196 [
197 'name' => 'dvz_sb_groups_recall',
198 'title' => 'Group permissions: Scroll back to show past shouts',
199 'description' => 'User groups that shoutbox will load previous posts when scrolling back for.',
200 'optionscode' => 'groupselect',
201 'value' => '-1',
202 ],
203 [
204 'name' => 'dvz_sb_groups_mod',
205 'title' => 'Group permissions: Moderation',
206 'description' => 'User groups that can moderate the Shoutbox (edit and delete shouts).',
207 'optionscode' => 'groupselect',
208 'value' => '',
209 ],
210 [
211 'name' => 'dvz_sb_groups_mod_own',
212 'title' => 'Group permissions: Moderation of own shouts',
213 'description' => 'Users groups whose members can edit and delete their own shouts.',
214 'optionscode' => 'groupselect',
215 'value' => '',
216 ],
217 [
218 'name' => 'dvz_sb_supermods',
219 'title' => 'Super moderators are Shoutbox moderators',
220 'description' => 'Automatically allow forum super moderators to moderate Shoutbox as well.',
221 'optionscode' => 'yesno',
222 'value' => '1',
223 ],
224 [
225 'name' => 'dvz_sb_blocked_users',
226 'title' => 'Banned users',
227 'description' => 'Comma-separated list of user IDs that are banned from posting messages.',
228 'optionscode' => 'textarea',
229 'value' => '',
230 ],
231 ];
232
233 $i = 1;
234
235 foreach ($settings as &$row) {
236 $row['gid'] = $settingGroupId;
237 $row['title'] = $db->escape_string($row['title']);
238 $row['description'] = $db->escape_string($row['description']);
239 $row['disporder'] = $i++;
240 }
241
242 $db->insert_query_multiple('settings', $settings);
243
244 rebuild_settings();
245
246 // templates
247 $templates = [
248 'dvz_shoutbox_panel' => '<div class="panel">
249<form>
250<input type="text" class="text" placeholder="{$lang->dvz_sb_default}" maxlength="{$maxlength}" autocomplete="off" />
251<input type="submit" style="display:none" />
252</form>
253</div>',
254
255 'dvz_shoutbox' => '<div id="shoutbox" class="front{$classes}">
256
257<div class="head">
258<strong>{$lang->dvz_sb_shoutbox}</strong>
259<p class="right"><a href="{$mybb->settings[\'bburl\']}/index.php?action=shoutbox_archive">« {$lang->dvz_sb_archivelink}</a></p>
260</div>
261
262<div class="body">
263
264{$panel}
265
266<div class="window" style="height:{$mybb->settings[\'dvz_sb_height\']}px">
267<div class="data">
268{$html}
269</div>
270</div>
271
272</div>
273
274<script type="text/javascript" src="{$mybb->settings[\'bburl\']}/jscripts/dvz_shoutbox.js"></script>
275{$javascript}
276
277</div>',
278
279 'dvz_shoutbox_archive' => '<html>
280<head>
281<title>{$lang->dvz_sb_archive}</title>
282{$headerinclude}
283</head>
284<body>
285{$header}
286
287<script type="text/javascript" src="{$mybb->settings[\'bburl\']}/jscripts/dvz_shoutbox.js"></script>
288{$javascript}
289
290{$modoptions}
291
292{$multipage}
293
294<br />
295
296<div id="shoutbox">
297
298<div class="head"><strong>{$lang->dvz_sb_archive}</strong></div>
299
300<div class="data">
301{$archive}
302</div>
303</div>
304
305<br />
306
307{$multipage}
308
309{$footer}
310</body>
311</html>',
312
313 'dvz_shoutbox_archive_modoptions' => '<table border="0" cellspacing="{$theme[\'borderwidth\']}" cellpadding="{$theme[\'tablespace\']}" class="tborder">
314<tr><td class="thead" colspan="2"><strong>{$lang->dvz_sb_mod}</strong></td></tr>
315<tr><td class="tcat">{$lang->dvz_sb_mod_banlist}</td><td class="tcat">{$lang->dvz_sb_mod_clear}</td></tr>
316<tr>
317<td class="trow1">
318<form action="" method="post">
319<input type="text" class="textbox" style="width:80%" name="banlist" value="{$blocked_users}"></textarea>
320<input type="hidden" name="postkey" value="{$mybb->post_code}" />
321<input type="submit" class="button" value="{$lang->dvz_sb_mod_banlist_button}" />
322</form>
323</td>
324<td class="trow1">
325<form action="" method="post">
326<select name="days">
327<option value="2">2 {$lang->days}</option>
328<option value="7">7 {$lang->days}</option>
329<option value="30">30 {$lang->days}</option>
330<option value="90">90 {$lang->days}</option>
331<option value="all">* {$lang->dvz_sb_mod_clear_all} *</option>
332</select>
333<input type="hidden" name="postkey" value="{$mybb->post_code}" />
334<input type="submit" class="button" value="{$lang->dvz_sb_mod_clear_button}" />
335</form>
336</td>
337</tr>
338</table>
339<br />',
340 ];
341
342 $data = [];
343
344 foreach ($templates as $name => $content) {
345 $data[] = [
346 'title' => $name,
347 'template' => $db->escape_string($content),
348 'sid' => -1,
349 'version' => 1,
350 'status' => '',
351 'dateline' => TIME_NOW,
352 ];
353 }
354
355 $db->insert_query_multiple('templates', $data);
356}
357
358function dvz_shoutbox_uninstall()
359{
360 global $db;
361
362 $settingGroupId = $db->fetch_field(
363 $db->simple_select('settinggroups', 'gid', "name='dvz_shoutbox'"),
364 'gid'
365 );
366
367 // delete settings
368 $db->delete_query('settinggroups', 'gid=' . $settingGroupId);
369 $db->delete_query('settings', 'gid=' . $settingGroupId);
370
371 rebuild_settings();
372
373 // delete templates
374 $db->delete_query('templates', "title IN('dvz_shoutbox', 'dvz_shoutbox_panel', 'dvz_shoutbox_archive', 'dvz_shoutbox_archive_modoptions')");
375
376 // delete data
377 $db->drop_table('dvz_shoutbox');
378}
379
380function dvz_shoutbox_is_installed()
381{
382 global $mybb;
383 return $mybb->settings['dvz_sb_num'] !== null;
384}
385
386
387class dvz_shoutbox
388{
389
390 // hooks
391 static function global_start()
392 {
393 global $mybb, $templatelist;
394
395 if (defined('THIS_SCRIPT') && THIS_SCRIPT == 'index.php' && self::access_view()) {
396
397 if (!empty($templatelist)) {
398 $templatelist .= ',';
399 }
400
401 if ($mybb->get_input('action') == 'shoutbox_archive') {
402 // archive templates
403
404 $templatelist .= 'dvz_shoutbox_archive,multipage,multipage_page,multipage_page_current,multipage_nextpage';
405
406 if (self::access_mod()) {
407 $templatelist .= ',dvz_shoutbox_archive_modoptions';
408 }
409
410 } else {
411 // index templates
412 $templatelist .= 'dvz_shoutbox,dvz_shoutbox_panel';
413 }
414
415 }
416 }
417
418 static function global_end()
419 {
420 global $mybb;
421
422 if ($mybb->get_input('action') == 'shoutbox_archive' && self::access_view()) {
423 return self::show_archive();
424 }
425 }
426
427 static function xmlhttp()
428 {
429 global $mybb, $db, $charset;
430
431 switch ($mybb->get_input('action')) {
432
433 case 'dvz_sb_get_updates':
434
435 $permissions = (
436 self::access_view() &&
437 self::access_refresh()
438 );
439
440 $handler = function () use ($mybb, $db) {
441
442 $syncConditions = $mybb->settings['dvz_sb_sync']
443 ? "OR s.modified >= " . (time() - $mybb->settings['dvz_sb_interval']) . " AND s.id BETWEEN " . abs($mybb->get_input('first', MyBB::INPUT_INT)) . " AND " . abs($mybb->get_input('last', MyBB::INPUT_INT))
444 : null
445 ;
446
447 $data = self::get_multiple("WHERE s.id > " . abs($mybb->get_input('last', MyBB::INPUT_INT)) . " AND s.text IS NOT NULL " . $syncConditions . " ORDER BY s.id DESC LIMIT " . abs((int)$mybb->settings['dvz_sb_num']));
448
449 $html = null; // JS-handled empty response
450 $sync = [];
451 $lastId = 0;
452
453 while ($row = $db->fetch_array($data)) {
454
455 if ($row['id'] <= $mybb->get_input('last', MyBB::INPUT_INT)) {
456 // sync update
457
458 $sync[ $row['id'] ] = $row['text'] === null
459 ? null
460 : self::parse($row['text'], $row['username'])
461 ;
462
463 } else {
464 // new shout
465
466 if ($lastId == 0) {
467 $lastId = $row['id'];
468 }
469
470 $shout = self::render_shout($row);
471
472 $html = $mybb->settings['dvz_sb_reversed']
473 ? $shout . $html
474 : $html . $shout
475 ;
476
477 }
478
479 }
480
481 if ($html != null || !empty($sync)) {
482
483 $response = [];
484
485 if ($html != null) {
486 $response['html'] = $html;
487 $response['last'] = $lastId;
488 }
489
490 if (!empty($sync)) {
491 $response['sync'] = $sync;
492 }
493
494 echo json_encode($response);
495
496 }
497 };
498
499 break;
500
501 case 'dvz_sb_recall':
502
503 $permissions = (
504 self::access_view() &&
505 self::access_refresh() &&
506 self::access_recall()
507 );
508
509 $handler = function () use ($mybb, $db) {
510
511 $data = self::get_multiple("WHERE s.id < " . abs($mybb->get_input('first', MyBB::INPUT_INT)) . " ORDER BY s.id DESC LIMIT " . abs((int)$mybb->settings['dvz_sb_num']));
512
513 $response = [];
514
515 $html = null; // JS-handled empty response
516 $firstId = 0;
517
518 while ($row = $db->fetch_array($data)) {
519
520 $firstId = $row['id'];
521
522 $shout = self::render_shout($row);
523
524 $html = $mybb->settings['dvz_sb_reversed']
525 ? $shout . $html
526 : $html . $shout
527 ;
528 }
529
530 if ($html != null) {
531 $response['html'] = $html;
532 }
533
534 if ($db->num_rows($data) < $mybb->settings['dvz_sb_num']) {
535 $response['end'] = 1;
536 }
537
538 if ($response) {
539 $response['first'] = $firstId;
540 }
541
542 echo json_encode($response);
543
544 };
545
546 break;
547
548 case 'dvz_sb_shout':
549
550 $permissions = (
551 self::access_shout() &&
552 verify_post_check($mybb->get_input('key'), true)
553 );
554
555 $handler = function () use ($mybb) {
556
557 // JS-handled error (Anti-flood)
558 if (!self::antiflood_pass() && !self::access_mod()) {
559 die('A');
560 }
561
562 self::shout([
563 'uid' => $mybb->user['uid'],
564 'text' => $mybb->get_input('text'),
565 'ipaddress' => my_inet_pton(get_ip()),
566 ]);
567 };
568
569 break;
570
571 case 'dvz_sb_get':
572
573 $data = self::get($mybb->get_input('id', MyBB::INPUT_INT));
574
575 $permissions = (
576 (
577 self::access_mod() ||
578 (self::access_mod_own() && $data['uid'] == $mybb->user['uid'])
579 ) &&
580 verify_post_check($mybb->get_input('key'), true)
581 );
582
583 $handler = function () use ($data) {
584 echo json_encode([
585 'text' => $data['text'],
586 ]);
587 };
588
589 break;
590
591 case 'dvz_sb_update':
592
593 $permissions = (
594 self::can_mod($mybb->get_input('id', MyBB::INPUT_INT)) &&
595 verify_post_check($mybb->get_input('key'), true)
596 );
597
598 $handler = function () use ($mybb) {
599 self::update($mybb->get_input('id', MyBB::INPUT_INT), $mybb->get_input('text'));
600 echo self::parse($mybb->get_input('text'), self::get_username($mybb->get_input('id', MyBB::INPUT_INT)));
601
602 };
603
604 break;
605
606 case 'dvz_sb_delete':
607
608 $permissions = (
609 self::can_mod($mybb->get_input('id', MyBB::INPUT_INT)) &&
610 verify_post_check($mybb->get_input('key'), true)
611 );
612
613 $handler = function () use ($mybb) {
614 self::delete($mybb->get_input('id', MyBB::INPUT_INT));
615 };
616
617 break;
618
619 }
620
621 if (isset($permissions)) {
622
623 if ($permissions == false) {
624 echo 'P'; // JS-handled error (Permissions)
625 } else {
626
627 header('Content-type: text/plain; charset=' . $charset);
628 header('Cache-Control: no-store'); // force update on load
629 $handler();
630
631 }
632
633 }
634 }
635
636 static function load_window()
637 {
638 global $templates, $dvz_shoutbox, $lang, $mybb, $db, $theme;
639
640 $lang->load('dvz_shoutbox');
641
642 // MyBB template
643 $dvz_shoutbox = null;
644
645 // dvz_shoutbox template
646 $javascript = null;
647 $panel = null;
648 $classes = null;
649
650 if (self::access_view()) {
651
652 if (self::is_user()) {
653
654 // message: blocked
655 if (self::is_blocked()) {
656 $panel = '<div class="panel blocked"><p>' . $lang->dvz_sb_user_blocked . '</p></div>';
657 }
658 // message: minimum posts
659 else if (!self::access_minposts() && !self::access_mod()) {
660 $panel = '<div class="panel minposts"><p>' . str_replace('{MINPOSTS}', (int)$mybb->settings['dvz_sb_minposts'], $lang->dvz_sb_minposts) . '</p></div>';
661 }
662 // shout form
663 else if (self::access_shout()) {
664 $maxlength = $mybb->settings['dvz_sb_maxlength'] ? (int)$mybb->settings['dvz_sb_maxlength'] : null;
665 eval('$panel = "' . $templates->get('dvz_shoutbox_panel') . '";');
666 }
667
668 }
669
670 $js = null;
671
672 // configuration
673 $js .= 'dvz_shoutbox.interval = ' . (self::access_refresh() ? (float)$mybb->settings['dvz_sb_interval'] : 0) . ';' . PHP_EOL;
674 $js .= 'dvz_shoutbox.antiflood = ' . (self::access_mod() ? 0 : (float)$mybb->settings['dvz_sb_antiflood']) . ';' . PHP_EOL;
675 $js .= 'dvz_shoutbox.maxShouts = ' . (int)$mybb->settings['dvz_sb_num'] . ';' . PHP_EOL;
676 $js .= 'dvz_shoutbox.awayTime = ' . (float)$mybb->settings['dvz_sb_away'] . '*1000;' . PHP_EOL;
677 $js .= 'dvz_shoutbox.lang = [\'' . $lang->dvz_sb_delete_confirm . '\', \'' . str_replace('{ANTIFLOOD}', (float)$mybb->settings['dvz_sb_antiflood'], $lang->dvz_sb_antiflood) . '\', \''.$lang->dvz_sb_permissions.'\'];' . PHP_EOL;
678
679 // reversed order
680 if ($mybb->settings['dvz_sb_reversed']) {
681 $js .= 'dvz_shoutbox.reversed = true;' . PHP_EOL;
682 $js .= '$(\'#shoutbox .window\').scrollTop( $(\'#shoutbox .window\')[0].scrollHeight );' . PHP_EOL;
683 }
684
685 // lazyload
686 if ($mybb->settings['dvz_sb_lazyload']) {
687 $js .= 'dvz_shoutbox.lazyMode = \'' . (int)$mybb->settings['dvz_sb_lazyload'] . '\';' . PHP_EOL;
688 $js .= '$(window).bind(\'scroll resize\', dvz_shoutbox.checkVisibility);' . PHP_EOL;
689 }
690
691 // away mode
692 if ($mybb->settings['dvz_sb_away']) {
693 $js .= '$(window).on(\'mousemove click dblclick keydown scroll\', dvz_shoutbox.updateActivity);' . PHP_EOL;
694 }
695
696 // shoutbox status
697 $status = !isset($mybb->cookies['dvz_sb_status']) || $mybb->cookies['dvz_sb_status'] == 1;
698
699 $js .= 'dvz_shoutbox.status = ' . (int)$status . ';' . PHP_EOL;
700
701 if ($status == false) {
702 $classes .= ' collapsed';
703 }
704
705 $html = null;
706 $firstId = 0;
707 $lastId = 0;
708
709 if ($status == true) {
710
711 // preloaded shouts
712 $data = self::get_multiple("WHERE s.text IS NOT NULL ORDER BY s.id DESC LIMIT " . abs((int)$mybb->settings['dvz_sb_num']));
713
714 while ($row = $db->fetch_array($data)) {
715
716 $firstId = $row['id'];
717
718 if ($lastId == 0) {
719 $lastId = $row['id'];
720 }
721
722 $shout = self::render_shout($row);
723
724 $html = $mybb->settings['dvz_sb_reversed']
725 ? $shout . $html
726 : $html . $shout
727 ;
728 }
729
730 }
731
732 if (self::access_refresh()) {
733 $js .= 'setTimeout(\'dvz_shoutbox.loop()\', ' . (float)$mybb->settings['dvz_sb_interval'] . ' * 1000);' . PHP_EOL;
734 }
735
736 if (self::access_recall()) {
737 $js .= 'dvz_shoutbox.recalling = true;' . PHP_EOL;
738 }
739
740 $javascript = '
741<script>
742' . $js . '
743dvz_shoutbox.firstId = ' . $firstId . ';
744dvz_shoutbox.lastId = ' . $lastId . ';
745dvz_shoutbox.appendControls();
746dvz_shoutbox.updateActivity();
747</script>';
748
749 eval('$dvz_shoutbox = "' . $templates->get('dvz_shoutbox') . '";');
750
751 }
752 }
753
754 static function show_archive()
755 {
756 global $db, $mybb, $templates, $lang, $theme, $footer, $headerinclude, $header, $charset;
757
758 $lang->load('dvz_shoutbox');
759
760 header('Content-type: text/html; charset=' . $charset);
761
762 add_breadcrumb($lang->dvz_sb_shoutbox, "index.php?action=shoutbox_archive");
763
764 // moderation panel
765 if (self::access_mod()) {
766
767 if ($mybb->get_input('banlist') && verify_post_check($mybb->get_input('postkey'))) {
768 self::banlist_update($mybb->get_input('banlist'));
769 }
770
771 if ($mybb->get_input('days') && verify_post_check($mybb->get_input('postkey'))) {
772 if ($mybb->get_input('days') == 'all') {
773 self::clear();
774 } else {
775 $allowed = [2, 7, 30, 90];
776 if (in_array($mybb->get_input('days'), $allowed)) {
777 self::clear($mybb->get_input('days'));
778 }
779 }
780 }
781
782 $blocked_users = htmlspecialchars_uni($mybb->settings['dvz_sb_blocked_users']);
783 eval('$modoptions = "' . $templates->get("dvz_shoutbox_archive_modoptions") . '";');
784
785 } else {
786 $modoptions = null;
787 }
788
789 // pagination
790 $items = self::count();
791 $page = abs($mybb->get_input('page', MyBB::INPUT_INT));
792 $perPage = abs((int)$mybb->settings['dvz_sb_num_archive']);
793
794 if ($perPage == 0) {
795 $pages = 0;
796 } else {
797 $pages = ceil($items / $perPage);
798 }
799
800 if (!$page || $page < 1 || $page > $pages) {
801 $page = 1;
802 }
803
804 $start = ($page - 1) * $perPage;
805
806 if ($items > $perPage) {
807 $multipage = multipage($items, $perPage, $page, 'index.php?action=shoutbox_archive');
808 }
809
810
811 $data = self::get_multiple("WHERE s.text IS NOT NULL ORDER by s.id DESC LIMIT $start,$perPage");
812
813 $archive = null;
814
815 while ($row = $db->fetch_array($data)) {
816 $archive .= self::render_shout($row, true);
817 }
818
819 $javascript = '
820<script>
821dvz_shoutbox.lang = [\'' . $lang->dvz_sb_delete_confirm . '\', \'' . str_replace('{ANTIFLOOD}', (float)$mybb->settings['dvz_sb_antiflood'], $lang->dvz_sb_antiflood) . '\', \'' . $lang->dvz_sb_permissions . '\'];
822</script>';
823
824 eval('$content = "' . $templates->get("dvz_shoutbox_archive") . '";');
825
826 output_page($content);
827
828 exit;
829 }
830
831 static function user_merge()
832 {
833 global $db, $source_user, $destination_user;
834 return $db->update_query('dvz_shoutbox', ['uid' => $destination_user['uid']], 'uid=' . (int)$source_user['uid']);
835 }
836
837 static function activity(&$user_activity)
838 {
839 $location = parse_url($user_activity['location']);
840 $filename = basename($location['path']);
841
842 parse_str(html_entity_decode($location['query']), $parameters);
843
844 if ($filename == 'index.php' && $parameters['action'] == 'shoutbox_archive') {
845 $user_activity['activity'] = 'dvz_shoutbox_archive';
846 }
847 }
848
849 static function activity_translate(&$data)
850 {
851 global $lang;
852
853 $lang->load('dvz_shoutbox');
854
855 if ($data['user_activity']['activity'] == 'dvz_shoutbox_archive') {
856 $data['location_name'] = sprintf($lang->dvz_sb_activity, 'index.php?action=shoutbox_archive');
857 }
858 }
859
860 static function clearcookies()
861 {
862 global $remove_cookies;
863 $remove_cookies[] = 'dvz_sb_status';
864 }
865
866 // data manipulation
867 static function get($id)
868 {
869 global $db;
870 return $db->fetch_array( $db->simple_select('dvz_shoutbox', '*', 'id=' . (int)$id) );
871 }
872
873 static function get_multiple($clauses)
874 {
875 global $db;
876 return $db->query("
877 SELECT
878 s.*, u.username, u.usergroup, u.displaygroup, u.avatar
879 FROM
880 " . TABLE_PREFIX . "dvz_shoutbox s
881 LEFT JOIN " . TABLE_PREFIX . "users u ON u.uid = s.uid
882 " . $clauses . "
883 ");
884 }
885
886 static function get_username($id)
887 {
888 global $db;
889 return $db->fetch_field(
890 $db->query("SELECT username FROM " . TABLE_PREFIX . "users u, " . TABLE_PREFIX . "dvz_shoutbox s WHERE u.uid=s.uid AND s.id=" . (int)$id),
891 'username'
892 );
893 }
894
895 static function user_last_shout_time($uid)
896 {
897 global $db;
898 return $db->fetch_field(
899 $db->simple_select('dvz_shoutbox', 'date', 'uid=' . (int)$uid, [
900 'order_by' => 'date',
901 'order_dir' => 'desc',
902 'limit' => 1,
903 ]),
904 'date'
905 );
906 }
907
908 static function count()
909 {
910 global $db;
911 return $db->fetch_field(
912 $db->simple_select('dvz_shoutbox', 'COUNT(*) as n'),
913 'n'
914 );
915 }
916
917 static function shout($data)
918 {
919 global $mybb, $db;
920
921 if ($mybb->settings['dvz_sb_maxlength'] > 0) {
922 $data['text'] = mb_substr($data['text'], 0, $mybb->settings['dvz_sb_maxlength']);
923 }
924
925 foreach ($data as &$item) {
926 $item = $db->escape_string($item);
927 }
928
929 $data['date'] = TIME_NOW;
930
931 return $db->insert_query('dvz_shoutbox', $data);
932 }
933
934 static function update($id, $text)
935 {
936 global $db;
937 return $db->update_query('dvz_shoutbox', [
938 'text' => $db->escape_string($text),
939 'modified' => time(),
940 ], 'id=' . (int)$id);
941 }
942
943 static function banlist_update($new)
944 {
945 global $db;
946
947 $db->update_query('settings', ['value' => $db->escape_string($new)], "name='dvz_sb_blocked_users'");
948
949 rebuild_settings();
950 }
951
952 static function delete($id)
953 {
954 global $mybb, $db;
955
956 if ($mybb->settings['dvz_sb_sync']) {
957 return $db->update_query('dvz_shoutbox', [
958 'text' => 'NULL',
959 'modified' => time(),
960 ], 'id=' . (int)$id, false, true);
961 } else {
962 return $db->delete_query('dvz_shoutbox', 'id=' . (int)$id);
963 }
964 }
965
966 static function clear($days = false)
967 {
968 global $db;
969
970 if ($days) {
971 $where = 'date < ' . ( TIME_NOW - ((int)$days * 86400) );
972 } else {
973 $where = false;
974 }
975
976 return $db->delete_query('dvz_shoutbox', $where);
977 }
978
979 // permissions
980 static function is_user()
981 {
982 global $mybb;
983 return $mybb->user['uid'] != 0;
984 }
985
986 static function is_blocked()
987 {
988 global $mybb;
989 return in_array($mybb->user['uid'], self::settings_get_csv('blocked_users'));
990 }
991
992 static function access_view()
993 {
994 $array = self::settings_get_csv('groups_view');
995 return $array[0] == -1 || is_member($array);
996 }
997
998 static function access_refresh()
999 {
1000 $array = self::settings_get_csv('groups_refresh');
1001 return $array[0] == -1 || is_member($array);
1002 }
1003
1004 static function access_shout()
1005 {
1006 $array = self::settings_get_csv('groups_shout');
1007
1008 return (
1009 self::is_user() &&
1010 !self::is_blocked() &&
1011 (
1012 self::access_mod() ||
1013 (
1014 self::access_view() &&
1015 self::access_minposts() &&
1016 $array[0] == -1 || is_member($array)
1017 )
1018 )
1019 );
1020 }
1021
1022 static function access_recall()
1023 {
1024 $array = self::settings_get_csv('groups_recall');
1025 return $array[0] == -1 || is_member($array);
1026 }
1027
1028 static function access_mod()
1029 {
1030 global $mybb;
1031
1032 $array = self::settings_get_csv('groups_mod');
1033
1034 return (
1035 ($array[0] == -1 || is_member($array)) ||
1036 ($mybb->settings['dvz_sb_supermods'] && $mybb->usergroup['issupermod'])
1037 );
1038 }
1039
1040 static function access_mod_own()
1041 {
1042 $array = self::settings_get_csv('groups_mod_own');
1043
1044 return $array[0] == -1 || is_member($array);
1045 }
1046
1047 static function access_minposts()
1048 {
1049 global $mybb;
1050 return $mybb->user['postnum'] >= $mybb->settings['dvz_sb_minposts'];
1051 }
1052
1053 static function can_mod($shoutId)
1054 {
1055 global $mybb;
1056
1057 if (self::access_mod()) {
1058 return true;
1059 } else if (self::access_mod_own() && self::access_shout()) {
1060
1061 $data = self::get($shoutId);
1062
1063 if ($data['uid'] == $mybb->user['uid']) {
1064 return true;
1065 }
1066
1067 }
1068
1069 return false;
1070
1071 }
1072
1073 // core
1074 static function render_shout($data, $static = false)
1075 {
1076 global $mybb;
1077
1078 $id = $data['id'];
1079 $text = self::parse($data['text'], $data['username']);
1080 if($data['usergroup'] == 8)
1081 $text = '<span class="globaladmin-shout">'.$text.'</span>';
1082 if($data['usergroup'] == 3)
1083 $text = '<span class="admin-shout">'.$text.'</span>';
1084 if($data['usergroup'] == 6)
1085 $text = '<span class="mod-shout">'.$text.'</span>';
1086 $date = htmlspecialchars_uni(my_date($mybb->settings['dvz_sb_dateformat'], $data['date']));
1087 $username = htmlspecialchars_uni($data['username']);
1088 $user = build_profile_link(format_name($username, $data['usergroup'], $data['displaygroup']), (int)$data['uid']);
1089 $avatar = '<img src="' . (empty($data['avatar']) ? htmlspecialchars_uni($mybb->settings['useravatar']) : htmlspecialchars_uni($data['avatar'])) . '" alt="avatar" />';
1090
1091 $notes = null;
1092 $attributes = null;
1093
1094 $own = $data['uid'] == $mybb->user['uid'];
1095
1096 if ($static) {
1097
1098 if (self::access_mod()) {
1099 $notes .= '<span class="ip">' . my_inet_ntop($data['ipaddress']) . '</span>';
1100 }
1101
1102 if (
1103 self::access_mod() ||
1104 (self::access_mod_own() && $own)
1105 ) {
1106 $notes .= '<a href="" class="mod edit">E</a><a href="" class="mod del">X</a>';
1107 }
1108
1109 }
1110
1111 if (
1112 self::access_mod() ||
1113 (self::access_mod_own() && $own)
1114 ) {
1115 $attributes .= ' data-mod';
1116 }
1117
1118 if ($own) {
1119 $attributes .= ' data-own';
1120 }
1121 if($mybb->user['ignorelist'] != '')
1122 {
1123 $ignlist = explode(",", $mybb->user['ignorelist']);
1124 if(!empty($ignlist) && in_array($data['uid'], $ignlist, true))
1125 {
1126 $text = '<span style="font-style: italic; font-size: 0.85em; color: red; font-weight: bold;">' . self::parse('Użytkownik jest przez ciebie ignorowany.', $data['username']) . '</span>';
1127 }
1128 }
1129
1130
1131 return '
1132<div class="entry" data-id="' . $id . '" data-username="' . $username . '"' . $attributes . '>
1133 <div class="avatar">' . $avatar . '</div>
1134 <div class="user">' . $user . '</div>
1135 <div class="text">' . $text . '</div>
1136 <div class="info">' . $notes . '<span class="date">' . $date . '</span></div>
1137</div>';
1138 }
1139
1140 static function parse($message, $me_username)
1141 {
1142 global $mybb;
1143
1144 require_once MYBB_ROOT . 'inc/class_parser.php';
1145
1146 $parser = new postParser;
1147 $options = [
1148 'allow_mycode' => $mybb->settings['dvz_sb_mycode'],
1149 'allow_smilies' => $mybb->settings['dvz_sb_smilies'],
1150 'allow_imgcode' => 0,
1151 'filter_badwords' => 1,
1152 'me_username' => $me_username,
1153 ];
1154
1155 return $parser->parse_message($message, $options);
1156 }
1157
1158 static function antiflood_pass()
1159 {
1160 global $mybb;
1161 return (
1162 !$mybb->settings['dvz_sb_antiflood'] ||
1163 ( TIME_NOW - self::user_last_shout_time($mybb->user['uid']) ) > $mybb->settings['dvz_sb_antiflood']
1164 );
1165 }
1166
1167 static function settings_get_csv($name)
1168 {
1169 global $mybb;
1170 return array_filter( explode(',', $mybb->settings['dvz_sb_' . $name]) );
1171 }
1172
1173}
1174function dvz_shoutbox_stats()
1175{
1176 global $db, $mybb, $templates, $theme, $wpisy, $users, $top_spamer, $users_online_o, $top_spamer_noformatted, $shshshs, $our_shouts, $timesearch, $shouts, $username, $user, $users_online, $anon_online, $invisiblemark, $onlinemembers, $guests_online, $spiders, $cache, $bots_online;
1177
1178 // Użytkownicy online
1179
1180 $timesearch = TIME_NOW - $mybb->settings['wolcutoff'];
1181 $comma = '';
1182 $query3 = $db->query("
1183 SELECT s.sid, s.ip, s.uid, s.time, s.location, s.location1, u.username, u.invisible, u.usergroup, u.displaygroup
1184 FROM ".TABLE_PREFIX."sessions s
1185 LEFT JOIN ".TABLE_PREFIX."users u ON (s.uid=u.uid)
1186 WHERE `location` LIKE '%shoutbox.php%' && s.time>'".$timesearch."'
1187 ORDER BY u.username ASC, s.time DESC
1188 ");
1189
1190 $spiders = $cache->read("spiders");
1191
1192 $users_online = 0;
1193 $anon_online = 0;
1194 $guests_online = 0;
1195 $bots_online = 0;
1196 $onlinemembers = '';
1197
1198 while($user = $db->fetch_array($query3))
1199 {
1200 if($user['uid'] > 0)
1201 {
1202 if($user['invisible'] == 1)
1203 {
1204 ++$anon_online;
1205 }
1206
1207 if($user['invisible'] != 1 || $mybb->usergroup['canviewwolinvis'] == 1 || $user['uid'] == $mybb->user['uid'])
1208 {
1209 if($user['invisible'] == 1)
1210 {
1211 $invisiblemark = "*";
1212 }
1213 else
1214 {
1215 $invisiblemark = '';
1216 }
1217 ++$anon_online;
1218
1219 $username = build_profile_link(format_name($user['username'], $user['usergroup'], $user['displaygroup']), $user['uid']);
1220 $onlinemembers .= ''.$comma.' '.$username.''.$invisiblemark.'';
1221 $comma = " ,";
1222 }
1223 ++$users_online;
1224 }
1225 elseif(my_strpos($user['sid'], "bot=") !== false && $spiders[$botkey])
1226 {
1227 // The user is a search bot.
1228 $onlinemembers .= $comma.format_name($spiders[$botkey]['name'], $spiders[$botkey]['usergroup']);
1229 $comma = ", ";
1230 ++$bots_online;
1231 }
1232 else
1233 {
1234 // The user is a guest.
1235 ++$guests_online;
1236 }
1237
1238 $users_online_o = $users_online + $guests_online;
1239 }
1240 // Statystyki
1241
1242 $query = $db->query("SELECT count(id) as id FROM ".TABLE_PREFIX."dvz_shoutbox");
1243
1244 $row = $db->fetch_array($query);
1245 $wpisy = $row['id'];
1246
1247 $query3 = $db->query("SELECT d.uid, u.username, u.usergroup, u.displaygroup, u.uid, u.avatar, count(*) as shouters
1248 FROM ".TABLE_PREFIX."dvz_shoutbox d
1249 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=d.uid)
1250 GROUP BY d.uid
1251 ORDER BY shouters
1252 DESC LIMIT 1");
1253 $row3 = $db->fetch_array($query3);
1254
1255 $shouts = $row3['shouters'];
1256 $top_spamer_noformatted = $row3['username'];
1257 $top_spamer = build_profile_link(format_name($row3['username'], $row3['usergroup'], $row3['displaygroup']), $row3['uid']);
1258
1259 $query4 = $db->query("SELECT count(id) as id, uid FROM ".TABLE_PREFIX."dvz_shoutbox WHERE uid='".$mybb->user['uid']."'");
1260
1261 $our_shouts = $db->fetch_field($query4, "id");
1262}
1263
1264function dvz_shoutbox_users_on_sb()
1265{
1266 global $db, $mybb, $templates, $theme, $users_o_sb, $timesearch;
1267
1268 $timesearch = TIME_NOW - $mybb->settings['wolcutoff'];
1269 $query = $db->query("SELECT count(*) as guid, time FROM ".TABLE_PREFIX."sessions WHERE `location` LIKE '%shoutbox.php%' && uid!=0 && time>".$timesearch."");
1270 $users_o_sb = $db->fetch_field($query, 'guid');
1271}