PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 All 138 releases
wpforo / classes / Logs.php

Logs.php in wpForo Forum 3.1.6, at classes/Logs.php

448 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\classes;
4
5 // Exit if accessed directly
6 if( ! defined( 'ABSPATH' ) ) exit;
7
8 class Logs {
9 public function read() {
10 $data = WPF()->current_object;
11
12 if( wpforo_setting( 'logging', 'view_logging' ) && wpfval( $data, 'template' ) ) {
13 if( $data['template'] === 'post' ) {
14 if( wpfval( $data, 'topicid' ) && wpfval( $data, 'topic', 'last_post' ) ) {
15 $this->read_item( $data['topicid'], $data['topic']['last_post'], wpforo_prefix( 'read_topics' ) );
16 }
17 if( wpfval( $data, 'forumid' ) ) {
18 $end_date = time() - ( 14 * 24 * 60 * 60 );
19 $items_count = 0;
20 $args = [ 'read' => false, 'forumid' => $data['forumid'], 'orderby' => 'modified', 'order' => 'ASC', 'row_count' => 1, 'where' => "`modified` > '" . gmdate( 'Y-m-d H:i:s', $end_date ) . "'" ];
21 $topics = WPF()->topic->get_topics( $args, $items_count );
22 if( empty( $topics ) ) {
23 if( wpfval( $data, 'forum', 'last_postid' ) ) {
24 $last_postid = $data['forum']['last_postid'];
25 } elseif( wpfval( $data, 'topic', 'last_post' ) ) {
26 $last_postid = $data['topic']['last_post'];
27 }
28 if( ! empty( $last_postid ) ) $this->read_item( $data['forumid'], $last_postid, wpforo_prefix( 'read_forums' ) );
29 } else {
30 foreach( $topics as $topic ) {
31 if( wpfval( $topic, 'last_post' ) ) {
32 if( $items_count == 1 && $topic['topicid'] == $data['topicid'] ) {
33 if( wpfval( $data, 'forum', 'last_postid' ) ) {
34 $last_postid = $data['forum']['last_postid'];
35 } elseif( wpfval( $data, 'topic', 'last_post' ) ) {
36 $last_postid = $data['topic']['last_post'];
37 } elseif( wpfval( $topic, 'last_post' ) ) {
38 $last_postid = $topic['last_post'];
39 }
40 } else {
41 $last_postid = ( $topic['last_post'] - 1 );
42 }
43 if( isset( $last_postid ) ) {
44 $this->read_item( $data['forumid'], $last_postid, wpforo_prefix( 'read_forums' ) );
45 break;
46 }
47 }
48 }
49 }
50 }
51 }
52 }
53 }
54
55 public function read_item( $itemid, $item_last_postid, $key ) {
56
57 $log = false;
58 $logdb = false;
59 $read_ids = [];
60 $read_db_ids = [];
61 $login = is_user_logged_in();
62
63 if( $itemid && $key ) {
64 if( wpforo_setting( 'legal', 'cookies' ) ) $read_ids = wpforo_getcookie( $key, false );
65 if( wpfval( WPF()->current_usermeta, $key ) ) $read_db_ids = wpforo_current_usermeta( $key );
66 if( ! $read_ids || ! is_array( $read_ids ) ) $read_ids = [];
67 if( ! $read_db_ids || ! is_array( $read_db_ids ) ) $read_db_ids = [];
68
69 if( empty( $read_ids ) || ( $login && empty( $read_db_ids ) ) ) {
70 if( empty( $read_ids ) ) $log = true;
71 if( empty( $read_db_ids ) && $login ) $logdb = true;
72 $read_ids = [ $itemid => $item_last_postid ];
73 } elseif( ! wpfval( $read_ids, $itemid ) || ! wpfval( $read_db_ids, $itemid ) || (int) $read_ids[ $itemid ] < (int) $item_last_postid || (int) $read_db_ids[ $itemid ] < (int) $item_last_postid ) {
74 $log = true;
75 if( $login ) $logdb = true;
76 if( is_array( $read_ids ) && is_array( $read_db_ids ) ) {
77 $keep_guest_read = apply_filters( 'wpforo_keep_guest_read', false );
78 $merge_guest_read = apply_filters( 'wpforo_merge_guest_read', true );
79 if( $merge_guest_read ) {
80 $read_ids = ( $keep_guest_read ) ? $read_ids + $read_db_ids : $read_db_ids + $read_ids;
81 } else {
82 $read_ids = ( $login ) ? $read_db_ids : $read_ids;
83 }
84 }
85 $read_ids[ $itemid ] = $item_last_postid;
86 if( $key === wpforo_prefix( 'read_forums' ) ) {
87 $read_ids = $this->read_parent_items( $read_ids, $itemid, $item_last_postid );
88 }
89 }
90 $this->log_item( $read_ids, $key, $logdb, $log );
91 }
92 }
93
94 public function read_parent_items( $read_ids = [], $itemid = 0, $item_last_postid = 0 ) {
95 if( wpfval( $read_ids, $itemid ) && $item_last_postid ) {
96 $parents = [];
97 WPF()->forum->get_parents( $itemid, $parents );
98 foreach( $parents as $parent ) {
99 $read_ids[ $parent ] = $item_last_postid;
100 }
101 }
102
103 return $read_ids;
104 }
105
106 public function log_item( $read_ids, $key, $logdb = true, $log = true ) {
107 if( $log && wpforo_setting( 'legal', 'cookies' ) ) {
108 wpforo_setcookie( $key, $read_ids, false );
109 }
110 if( $logdb && is_user_logged_in() ) {
111 $max = apply_filters( 'wpforo_max_logged_topics', 100 );
112 $num = count( $read_ids );
113 if( $num > $max ) {
114 $delta = $num - $max;
115 if( $delta > 0 ) $read_ids = array_slice( $read_ids, $delta, null, true );
116 }
117 update_user_meta( WPF()->current_userid, $key, $read_ids );
118 }
119 }
120
121 public function unread( $id, $in = 'forum', $postid = 0 ) {
122
123 $new = false;
124 if( ! wpforo_setting( 'logging', 'view_logging' ) ) return false;
125
126 if( $id ) {
127 if( $in === 'forum' ) {
128 $last_postid = wpforo_forum( $id, 'last_postid' );
129 if( ! $last_postid ) {
130 return false;
131 }
132 //Pass all posts created before "Mark all read" action
133 $last_read_postid = $this->get_all_read( 'post' );
134 if( $last_read_postid ) {
135 if( (int) $last_postid > $last_read_postid ) $new = true;
136 }
137 //Check the last read post of current forum
138 if( ! $last_read_postid || $new ) {
139 $read_forums = $this->get_read_forums();
140 if( wpfkey( $read_forums, $id ) ) {
141 $last_read_postid = $read_forums[ $id ];
142 if( (int) $last_postid > (int) $last_read_postid ) {
143 $new = true;
144 } else {
145 $new = false;
146 }
147 } else {
148 $new = true;
149 }
150 }
151 $new = apply_filters( 'wpforo_new_in_forum', $new, $id );
152 } elseif( $in == 'topic' || $in == 'post' ) {
153 $last_postid = wpforo_topic( $id, 'last_post' );
154 if( ! $last_postid ) {
155 return false;
156 }
157 //Pass all posts created before "Mark all read" action
158 $last_read_postid = $this->get_all_read( 'post' );
159 if( $last_read_postid ) {
160 if( (int) $last_postid > $last_read_postid ) $new = true;
161 }
162 //Check the last read post of current forum
163 if( ! $last_read_postid || $new ) {
164 $read_topics = $this->get_read_topics();
165 if( wpfkey( $read_topics, $id ) ) {
166 $last_read_postid = $read_topics[ $id ];
167 if( (int) $last_postid > (int) $last_read_postid ) {
168 $new = true;
169 } else {
170 $new = false;
171 }
172 } else {
173 $new = true;
174 }
175 }
176 //Check an individual post if it's unread in an unread topic
177 if( $in === 'post' && $postid && (int) $postid > (int) $last_read_postid ) $new = true;
178 $new = apply_filters( 'wpforo_new_in_topic', $new, $id );
179 }
180 }
181
182 return $new;
183 }
184
185 public function get_read( $return = 'topicid' ) {
186 $topic_ids = [];
187 $read_topics = $this->get_read_topics();
188 if( ! empty( $read_topics ) && is_array( $read_topics ) ) {
189 $last_read_postid = $this->get_all_read( 'post' );
190
191 // First pass: filter out topics already covered by all_read
192 $topics_to_check = [];
193 foreach( $read_topics as $topicid => $postid ) {
194 if( $last_read_postid && (int) $postid <= $last_read_postid ) {
195 unset( $read_topics[ $topicid ] );
196 } else {
197 $topics_to_check[ $topicid ] = $postid;
198 }
199 }
200
201 // Batch fetch last_post for all topics in a single query
202 $current_last_posts = [];
203 if( ! empty( $topics_to_check ) ) {
204 $ids_sql = implode( ',', array_map( 'intval', array_keys( $topics_to_check ) ) );
205 $rows = WPF()->db->get_results(
206 "SELECT `topicid`, `last_post` FROM `" . WPF()->tables->topics . "` WHERE `topicid` IN (" . $ids_sql . ")",
207 ARRAY_A
208 );
209 foreach( $rows as $row ) {
210 $current_last_posts[ (int) $row['topicid'] ] = (int) $row['last_post'];
211 }
212 }
213
214 // Second pass: compare using batch-fetched data
215 foreach( $topics_to_check as $topicid => $postid ) {
216 $current_last_postid = isset( $current_last_posts[ $topicid ] ) ? $current_last_posts[ $topicid ] : 0;
217 if( $current_last_postid ) {
218 if( (int) $current_last_postid <= (int) $postid ) {
219 $topic_ids[] = $topicid;
220 }
221 } else {
222 $topic_ids[] = $topicid;
223 }
224 }
225 }
226 if( $return == 'topicid' ) {
227 return $topic_ids;
228 } else {
229 return $read_topics;
230 }
231 }
232
233 public function get_read_forums() {
234 if( is_user_logged_in() && wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_forums' ) ) ) {
235 $read_forums = wpforo_current_usermeta( wpforo_prefix( 'read_forums' ) );
236 } else {
237 $read_forums = wpforo_getcookie( wpforo_prefix( 'read_forums' ), false );
238 }
239
240 return $read_forums;
241 }
242
243 public function get_read_topics() {
244 if( is_user_logged_in() && wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_topics' ) ) ) {
245 $read_topics = wpforo_current_usermeta( wpforo_prefix( 'read_topics' ) );
246 } else {
247 $read_topics = wpforo_getcookie( wpforo_prefix( 'read_topics' ), false );
248 }
249
250 return $read_topics;
251 }
252
253 public function last( $item ) {
254 $id = 0;
255
256 if( $item === 'forum' ) {
257 $id = WPF()->db->get_var( "SELECT MAX(`forumid`) FROM " . WPF()->tables->forums );
258 } elseif( $item === 'topic' ) {
259 $id = WPF()->db->get_var( "SELECT MAX(`topicid`) FROM " . WPF()->tables->topics );
260 } elseif( $item === 'post' ) {
261 $id = WPF()->db->get_var( "SELECT MAX(`postid`) FROM " . WPF()->tables->posts );
262 }
263
264 return intval( $id );
265 }
266
267 public function mark_all_read() {
268 $last_forumid = $this->last( 'forum' );
269 $last_topicid = $this->last( 'topic' );
270 $last_postid = $this->last( 'post' );
271 $last = [ 'forum' => $last_forumid, 'topic' => $last_topicid, 'post' => $last_postid ];
272 if( is_user_logged_in() ) {
273 update_user_meta( WPF()->current_userid, wpforo_prefix( 'all_read' ), $last );
274 update_user_meta( WPF()->current_userid, wpforo_prefix( 'read_forums' ), [] );
275 update_user_meta( WPF()->current_userid, wpforo_prefix( 'read_topics' ), [] );
276 }
277 wpforo_setcookie( wpforo_prefix( 'all_read' ), $last );
278 wpforo_setcookie( wpforo_prefix( 'read_forums' ) );
279 wpforo_setcookie( wpforo_prefix( 'read_topics' ) );
280 }
281
282 public function get_all_read( $item ) {
283 if( is_user_logged_in() ) {
284 $last = wpforo_current_usermeta( wpforo_prefix( 'all_read' ) );
285 } else {
286 $last = wpforo_getcookie( wpforo_prefix( 'all_read' ) );
287 }
288 if( $item && wpfval( $last, $item ) ) {
289 return intval( $last[ $item ] );
290 }
291
292 return 0;
293 }
294
295 public function visitors( $item ) {
296 $data = [];
297 $visitors = [];
298 if( wpfval( $item, 'topicid' ) ) {
299 $keep_vistors_data = apply_filters( 'wpforo_keep_visitors_data', 4000 );
300 $time = (int) time() - (int) $keep_vistors_data;
301 $visitors = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->visits . "` WHERE `topicid` = " . intval( $item['topicid'] ) . " AND `time` > " . $time . " ORDER BY `id` DESC", ARRAY_A );
302 } elseif( wpfval( $item, 'forumid' ) ) {
303 $keep_vistors_data = apply_filters( 'wpforo_keep_visitors_data', 4000 );
304 $time = (int) time() - (int) $keep_vistors_data;
305 $visitors = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->visits . "` WHERE `forumid` = " . intval( $item['forumid'] ) . " AND `time` > " . $time . " ORDER BY `id` DESC", ARRAY_A );
306 }
307 if( ! empty( $visitors ) ) {
308 $online_period = (int) time() - (int) wpforo_setting( 'profiles', 'online_status_timeout' );
309
310 // Batch-fetch the latest visit time per user to avoid N+1 queries
311 $online_user_ids = [];
312 foreach( $visitors as $visitor ) {
313 if( wpfval( $visitor, 'userid' ) && (int) $visitor['time'] >= $online_period ) {
314 $online_user_ids[ intval( $visitor['userid'] ) ] = true;
315 }
316 }
317 $user_max_times = [];
318 if( ! empty( $online_user_ids ) ) {
319 $id_list = implode( ',', array_keys( $online_user_ids ) );
320 $rows = WPF()->db->get_results(
321 "SELECT `userid`, MAX(`time`) as max_time FROM `" . WPF()->tables->visits . "` WHERE `userid` IN (" . $id_list . ") GROUP BY `userid`",
322 ARRAY_A
323 );
324 foreach( $rows as $row ) {
325 $user_max_times[ (int) $row['userid'] ] = (int) $row['max_time'];
326 }
327 }
328
329 foreach( $visitors as $visitor ) {
330 if( wpfval( $visitor, 'userid' ) ) {
331 if( (int) $visitor['time'] < $online_period ) {
332 $data['users']['viewed'][] = $visitor;
333 } else {
334 $uid = intval( $visitor['userid'] );
335 $max_time = isset( $user_max_times[ $uid ] ) ? $user_max_times[ $uid ] : 0;
336 if( $max_time > (int) $visitor['time'] ) {
337 $data['users']['viewed'][] = $visitor;
338 } else {
339 $data['users']['viewing'][] = $visitor;
340 }
341 }
342 } elseif( (int) $visitor['time'] > $online_period ) {
343 $data['guests'][] = $visitor;
344 }
345 }
346 }
347
348 return $data;
349 }
350
351 public function visit() {
352 if( wpforo_setting( 'logging', 'track_logging' ) ) {
353 if( ( wpforo_current_user_is( 'admin' ) || WPF()->current_user_groupid == 1 ) && ! wpforo_setting( 'logging', 'display_admin_viewers' ) ) {
354 return false;
355 }
356 $data = WPF()->current_object;
357 $visitor = WPF()->current_user;
358 $template = ( wpfval( $data, 'template' ) ) ? $data['template'] : '';
359 $templates = [ 'forum', 'topic', 'post' ];
360 $templates = apply_filters( 'wpforo_track_visitors_in_pages', $templates );
361 if( $template ) {
362 if( $template == 'post' && in_array( $template, $templates ) ) {
363 //topic page (post list)
364 if( wpfval( $data, 'topic', 'topicid' ) && wpfval( $data, 'topic', 'forumid' ) ) {
365 $forumid = intval( $data['topic']['forumid'] );
366 $topicid = intval( $data['topic']['topicid'] );
367 $this->add_visit( $visitor, $forumid, $topicid );
368 }
369 } elseif( $template == 'topic' && in_array( $template, $templates ) ) {
370 //forum page (topic list)
371 if( wpfval( $data, 'forum', 'forumid' ) ) {
372 $forumid = intval( $data['forum']['forumid'] );
373 $this->add_visit( $visitor, $forumid );
374 }
375 } elseif( $template == 'forum' && in_array( $template, $templates ) ) {
376 //forum home page
377 $this->add_visit( $visitor );
378 } elseif( in_array( $template, $templates ) ) {
379 //other pages (profile, search, members, etc..)
380 $this->add_visit( $visitor );
381 }
382 }
383 }
384 return false;
385 }
386
387 public function add_visit( $visitor, $forumid = 0, $topicid = 0 ) {
388 $time = esc_sql( time() );
389 if( WPF()->current_userid ) {
390 $userid = ( wpfval( $visitor, 'userid' ) ) ? intval( $visitor['userid'] ) : 0;
391 $name = ( wpfval( $visitor, 'display_name' ) ) ? esc_sql( $visitor['display_name'] ) : '';
392 WPF()->db->query( "INSERT INTO `" . WPF()->tables->visits . "` (`userid`, `name`, `time`, `topicid`, `forumid`) VALUES( " . $userid . ", '" . $name . "', '" . $time . "', " . $topicid . ", " . $forumid . " ) ON DUPLICATE KEY UPDATE `time` = '" . $time . "'" );
393 } elseif( ! wpforo_is_bot() ) {
394 $ip = ( wpfval( $_SERVER, 'REMOTE_ADDR' ) ) ? substr( md5( (string) $_SERVER['REMOTE_ADDR'] ), 0, 32 ) : '-noip-';
395 if( $ip ) {
396 $guest = WPF()->db->get_var( "SELECT `id` FROM `" . WPF()->tables->visits . "` WHERE `ip` = '" . esc_sql( $ip ) . "' LIMIT 1" );
397 if( $guest ) {
398 WPF()->db->query( "UPDATE `" . WPF()->tables->visits . "` SET `topicid` = " . intval( $topicid ) . ", `forumid` = " . intval( $forumid ) . ", `time` = '" . $time . "' WHERE `id` = " . intval( $guest ) );
399 } else {
400 WPF()->db->query( "INSERT IGNORE INTO `" . WPF()->tables->visits . "` (`ip`, `time`, `topicid`, `forumid`) VALUES( '" . esc_sql( $ip ) . "', '" . $time . "', " . $topicid . ", " . $forumid . " )" );
401 }
402 }
403 }
404 }
405
406 /**
407 * do not use, this logic is not tested yet.
408 *
409 * @param int $forumid
410 *
411 * @return int
412 */
413 public function get_forum_unreads_count( $forumid ) {
414 $forumid = intval( $forumid );
415 $read_forums = $this->get_read_forums();
416 $last_read_postid = (int) wpfval( $read_forums, $forumid );
417 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->posts . "`
418 WHERE `status` = 0
419 AND `private` = 0
420 AND `forumid` = $forumid
421 AND `postid` > $last_read_postid";
422 $count = (int) WPF()->db->get_var( $sql );
423
424 return (int) apply_filters( 'wpforo_logs_get_forum_unreads_count', $count, $forumid, $last_read_postid );
425 }
426
427 /**
428 * get unread posts count for topic by giving topicid
429 *
430 * @param int $topicid
431 *
432 * @return int
433 */
434 public function get_topic_unreads_count( $topicid ) {
435 $topicid = intval( $topicid );
436 $read_topics = $this->get_read_topics();
437 $last_read_postid = (int) wpfval( $read_topics, $topicid );
438 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->posts . "`
439 WHERE `status` = 0
440 AND `topicid` = $topicid
441 AND `postid` > $last_read_postid";
442 $count = (int) WPF()->db->get_var( $sql );
443
444 return (int) apply_filters( 'wpforo_logs_get_topic_unreads_count', $count, $topicid, $last_read_postid );
445 }
446
447 }
448