PluginProbe
wpForo Forum / 2.2.9
wpForo Forum v2.2.9
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 1.4.2 All 137 releases
wpforo / classes / Posts.php

Posts.php in wpForo Forum 2.2.9, at classes/Posts.php

2,225 lines 78.6 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 Posts {
9 private $fields = [];
10
11 private $touched_postids = [];
12
13 public static $cache = [ 'posts' => [], 'post' => [], 'item' => [], 'topic_slug' => [], 'forum_slug' => [], 'post_url' => [] ];
14
15 function __construct() {
16 $this->init_hooks();
17 }
18
19 public function get_cache( $var ) {
20 if( isset( self::$cache[ $var ] ) ) return self::$cache[ $var ];
21 }
22
23 public function reset() {
24 self::$cache = [ 'posts' => [], 'post' => [], 'item' => [], 'topic_slug' => [], 'forum_slug' => [], 'post_url' => [] ];
25 }
26
27 private function init_hooks() {
28 add_filter( 'wpforo_content_after', [ $this, 'print_custom_fields' ], 99, 2 );
29 add_action( 'wpforo_after_delete_user', [ $this, 'after_delete_user' ], 10, 2 );
30 add_action( 'wpforo_before_delete_reaction', [ $this, 'before_delete_reaction' ], 10, 2 );
31 add_action( 'wpforo_after_delete_reaction', [ $this, 'after_delete_reaction' ] );
32 add_action( 'wpforo_after_add_reaction', [ $this, 'after_add_reaction' ] );
33 }
34
35 /**
36 * @param int $layout
37 *
38 * @return int items_per_page
39 */
40 public function get_option_items_per_page( $layout = null ) {
41 switch( $layout ) {
42 case 4:
43 $items_per_page = wpforo_setting( 'topics', 'layout_threaded_posts_per_page' );
44 break;
45 case 3:
46 $items_per_page = wpforo_setting( 'topics', 'layout_qa_posts_per_page' );
47 break;
48 default:
49 $items_per_page = wpforo_setting( 'topics', 'posts_per_page' );
50 break;
51 }
52
53 return (int) apply_filters( 'wpforo_post_get_option_items_per_page', $items_per_page, $layout );
54 }
55
56 /**
57 * @param int $layout
58 *
59 * @return bool
60 */
61 public function get_option_union_first_post( $layout ) {
62 $layout = intval( $layout );
63 $union_first_post = (bool) wpforo_setting( 'topics', 'union_first_post', $layout );
64
65 return (bool) apply_filters( 'wpforo_post_options_get_union_first_post', $union_first_post, $layout );
66 }
67
68 public function add( $args = [] ) {
69
70 $guestposting = false;
71 $root_exists = wpforo_root_exist();
72
73 if( empty( $args ) && empty( $_REQUEST['post'] ) ) {
74 WPF()->notice->add( 'Reply request error', 'error' );
75
76 return false;
77 }
78 if( empty( $args ) && ! empty( $_REQUEST['post'] ) ) $args = $_REQUEST['post'];
79 if( ! isset( $args['body'] ) || ! $args['body'] ) {
80 WPF()->notice->add( 'Post is empty', 'error' );
81
82 return false;
83 }
84 if( ! wpfval( $args, 'title' ) && wpfval( $args, 'topicid' ) ) {
85 $args['title'] = wpforo_phrase( 'RE', false ) . ': ' . wpforo_topic( $args['topicid'], 'title' );
86 }
87 $args['name'] = ( isset( $args['name'] ) ? strip_tags( (string) $args['name'] ) : '' );
88 $args['email'] = ( isset( $args['email'] ) ? sanitize_email( $args['email'] ) : '' );
89 if( isset( $args['userid'] ) && $args['userid'] == 0 && $args['name'] && $args['email'] ) $guestposting = true;
90
91 extract( $args );
92
93 if( ! isset( $topicid ) || ! $topicid ) {
94 WPF()->notice->add( 'Error: No topic selected', 'error' );
95
96 return false;
97 }
98 if( ! $topic = WPF()->topic->get_topic( intval( $topicid ) ) ) {
99 WPF()->notice->add( 'Error: Topic is not found', 'error' );
100
101 return false;
102 }
103 if( ! $forum = WPF()->forum->get_forum( intval( $topic['forumid'] ) ) ) {
104 WPF()->notice->add( 'Error: Forum is not found', 'error' );
105
106 return false;
107 }
108
109 if( $topic['closed'] ) {
110 WPF()->notice->add( 'Can\'t write a post: This topic is closed', 'error' );
111
112 return false;
113 }
114
115 if( ! $guestposting && ! ( WPF()->perm->forum_can( 'cr', $topic['forumid'] ) || ( wpforo_is_owner( $topic['userid'], $topic['email'] ) && WPF()->perm->forum_can( 'ocr', $topic['forumid'] ) ) ) ) {
116 WPF()->notice->add( 'You don\'t have permission to create post in this forum', 'error' );
117
118 return false;
119 }
120
121 if( ! WPF()->perm->can_post_now() ) {
122 WPF()->notice->add( 'You are posting too quickly. Slow down.', 'error' );
123
124 return false;
125 }
126
127 if( ! WPF()->current_userid && $args['email'] ) WPF()->member->set_guest_cookies( $args );
128
129 do_action( 'wpforo_start_add_post', $args );
130
131 $post = $args;
132 $post['forumid'] = $forumid = ( isset( $topic['forumid'] ) ? intval( $topic['forumid'] ) : 0 );
133 $post['parentid'] = $parentid = ( isset( $parentid ) ? intval( $parentid ) : 0 );
134 $post['title'] = $title = ( isset( $title ) ? wpforo_text( trim( (string) $title ), 250, false ) : '' );
135 $post['body'] = $body = ( isset( $body ) ? preg_replace( '#</pre>[\r\n\t\s\0]*<pre>#isu', "\r\n", (string) $body ) : '' );
136 $post['created'] = $created = ( isset( $created ) ? $created : current_time( 'mysql', 1 ) );
137 $post['userid'] = $userid = ( isset( $userid ) ? intval( $userid ) : WPF()->current_userid );
138 if( $root_exists ) {
139 $post['root'] = ( $parentid ) ? ( isset( $root ) ? intval( $root ) : $this->get_root( $parentid ) ) : - 1;
140 } else {
141 $root = null;
142 }
143
144 $post = apply_filters( 'wpforo_add_post_data_filter', $post );
145
146 if( empty( $post ) ) return false;
147
148 extract( $post, EXTR_OVERWRITE );
149
150 if( isset( $forumid ) ) $forumid = intval( $forumid );
151 if( isset( $topicid ) ) $topicid = intval( $topicid );
152 if( isset( $parentid ) ) $parentid = intval( $parentid );
153 if( isset( $title ) ) $title = sanitize_text_field( trim( $title ) );
154 if( isset( $created ) ) $created = sanitize_text_field( $created );
155 if( isset( $userid ) ) $userid = intval( $userid );
156 if( isset( $body ) ) $body = wpforo_kses( trim( (string) $body ), 'post' );
157 $status = ( isset( $status ) && $status ? 1 : 0 );
158 $private = ( isset( $topic['private'] ) && $topic['private'] ? 1 : 0 );
159 if( isset( $name ) ) $name = strip_tags( trim( (string) $name ) );
160 if( isset( $email ) ) $email = strip_tags( trim( (string) $email ) );
161
162 do_action( 'wpforo_before_add_post', $post );
163
164 $fields = [
165 'forumid' => $forumid,
166 'topicid' => $topicid,
167 'parentid' => $parentid,
168 'userid' => $userid,
169 'title' => stripslashes( $title ),
170 'body' => stripslashes( (string) $body ),
171 'created' => $created,
172 'modified' => $created,
173 'status' => $status,
174 'private' => $private,
175 'name' => $name,
176 'email' => $email,
177 'root' => $root,
178 ];
179
180 $values = [ '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%d' ];
181
182 if( ! $root_exists ) {
183 unset( $fields['root'] );
184 unset( $fields[12] );
185 }
186
187 if( WPF()->db->insert(
188 WPF()->tables->posts,
189 $fields,
190 $values
191 ) ) {
192 $postid = WPF()->db->insert_id;
193
194 $post['postid'] = $postid;
195 $post['status'] = $status;
196 $post['private'] = $private;
197 $post['posturl'] = $this->get_url( $postid );
198
199 if( $root_exists ) WPF()->topic->rebuild_threads( $topic, $root );
200
201 $post = apply_filters( 'wpforo_after_add_post_filter', $post, $topic, $forum );
202 do_action( 'wpforo_after_add_post', $post, $topic, $forum );
203
204 wpforo_clean_cache( 'post', $postid, $post );
205 WPF()->notice->add( 'You successfully replied', 'success' );
206
207 return $postid;
208 }
209
210 WPF()->notice->add( 'Reply request error', 'error' );
211
212 return false;
213 }
214
215 public function edit( $args = [] ) {
216
217 //This variable will be based on according CAN of guest usergroup once Guest Posing is ready
218 $guestposting = false;
219
220 if( empty( $args ) && ( ! isset( $_REQUEST['post'] ) || empty( $_REQUEST['post'] ) ) ) return false;
221 if( empty( $args ) && ! empty( $_REQUEST['post'] ) ) $args = $_REQUEST['post'];
222 if( isset( $args['name'] ) ) {
223 $args['name'] = strip_tags( (string) $args['name'] );
224 }
225 if( isset( $args['email'] ) ) {
226 $args['email'] = sanitize_email( $args['email'] );
227 }
228
229 do_action( 'wpforo_start_edit_post', $args );
230
231 if( ! isset( $args['postid'] ) || ! $args['postid'] || ! is_numeric( $args['postid'] ) ) {
232 WPF()->notice->add( 'Cannot update post data', 'error' );
233
234 return false;
235 }
236 $args['postid'] = intval( $args['postid'] );
237 if( ! $post = $this->get_post( $args['postid'] ) ) {
238 WPF()->notice->add( 'No Posts found for update', 'error' );
239
240 return false;
241 }
242 if( ! $topic = WPF()->topic->get_topic( $post['topicid'] ) ) {
243 WPF()->notice->add( 'No Topic found for update', 'error' );
244
245 return false;
246 }
247 if( ! $forum = WPF()->forum->get_forum( $topic['forumid'] ) ) {
248 WPF()->notice->add( 'No Forum found for update', 'error' );
249
250 return false;
251 }
252
253 if( ! is_user_logged_in() ) {
254 if( ! isset( $post['email'] ) || ! $post['email'] ) {
255 WPF()->notice->add( 'Permission denied', 'error' );
256
257 return false;
258 } elseif( ! wpforo_current_guest( $post['email'] ) ) {
259 WPF()->notice->add( 'You are not allowed to edit this post', 'error' );
260
261 return false;
262 }
263 if( ! $args['name'] || ! $args['email'] ) {
264 WPF()->notice->add( 'Please insert required fields!', 'error' );
265
266 return false;
267 } else {
268 WPF()->member->set_guest_cookies( $args );
269 }
270 }
271
272 $args['userid'] = $post['userid'];
273 $args['status'] = $post['status'];
274
275 if( isset( $args['userid'] ) && $args['userid'] == 0 && isset( $args['name'] ) && isset( $args['email'] ) ) $guestposting = true;
276
277 $args = apply_filters( 'wpforo_edit_post_data_filter', $args );
278 if( empty( $args ) ) return false;
279
280 extract( $args, EXTR_OVERWRITE );
281
282 if( ! $guestposting ) {
283 $diff = time() - strtotime( $post['created'] . ' GMT' );
284 if( ! ( WPF()->perm->forum_can( 'er', $post['forumid'] ) || ( WPF()->current_userid == $post['userid'] && WPF()->perm->forum_can( 'eor', $post['forumid'] ) ) ) ) {
285 WPF()->notice->add( 'You don\'t have permission to edit post from this forum', 'error' );
286
287 return false;
288 }
289
290 if( ! WPF()->perm->forum_can( 'er', $post['forumid'] ) && wpforo_setting( 'posting', 'edit_own_post_durr' ) !== 0 && $diff > wpforo_setting( 'posting', 'edit_own_post_durr' ) ) {
291 WPF()->notice->add( 'The time to edit this post is expired.', 'error' );
292
293 return false;
294 }
295 }
296
297 $title = ( isset( $title ) ? wpforo_text( trim( (string) $title ), 250, false ) : '' );
298 $body = ( isset( $body ) ? preg_replace( '#</pre>[\r\n\t\s\0]*<pre>#isu', "\r\n", (string) $body ) : '' );
299 $body = wpforo_kses( trim( (string) $body ), 'post' );
300
301 $topicid = wpforo_bigintval( ( isset( $topicid ) ? $topicid : $post['topicid'] ) );
302 $title = trim( $title ) ? stripslashes( sanitize_text_field( trim( $title ) ) ) : stripslashes( (string) $post['title'] );
303 $body = $body ? stripslashes( (string) $body ) : stripslashes( (string) $post['body'] );
304 $status = isset( $status ) ? intval( $status ) : intval( $post['status'] );
305 $private = isset( $private ) ? intval( $private ) : intval( $post['private'] );
306 $name = isset( $name ) ? stripslashes( strip_tags( trim( (string) $name ) ) ) : stripslashes( (string) $post['name'] );
307 $email = isset( $email ) ? stripslashes( strip_tags( trim( (string) $email ) ) ) : stripslashes( (string) $post['email'] );
308
309 if( false !== WPF()->db->update( WPF()->tables->posts,
310 [
311 'title' => $title,
312 'body' => $body,
313 'modified' => current_time(
314 'mysql',
315 1
316 ),
317 'status' => $status,
318 'name' => $name,
319 'email' => $email,
320 ],
321 [ 'postid' => $postid ],
322 [ '%s', '%s', '%s', '%d', '%s', '%s' ],
323 [ '%d' ] ) ) {
324 $post['topicid'] = $topicid;
325 $post['title'] = $title;
326 $post['body'] = $body;
327 $post['status'] = $status;
328 $post['private'] = $private;
329 $post['name'] = $name;
330 $post['email'] = $email;
331 do_action( 'wpforo_after_edit_post', $post, $topic, $forum, $args );
332
333 wpforo_clean_cache( 'post', $postid, $post );
334 WPF()->notice->add( 'This post successfully edited', 'success' );
335
336 return $postid;
337 }
338
339 WPF()->notice->add( 'Reply request error', 'error' );
340
341 return false;
342 }
343
344 #################################################################################
345
346 /**
347 * Delete post from DB
348 * Returns true if successfully deleted or false.
349 *
350 * @param int $postid
351 * @param bool $delete_cache
352 * @param bool $rebuild_data
353 * @param array &$exclude
354 * @param bool $check_permissions
355 *
356 * @return bool
357 * @since 1.0.0
358 *
359 */
360 function delete( $postid, $delete_cache = true, $rebuild_data = true, &$exclude = [], $check_permissions = true ) {
361 $postid = intval( $postid );
362 $exclude = (array) $exclude;
363
364 if( ! $post = $this->get_post( $postid ) ) return true;
365
366 do_action( 'wpforo_before_delete_post', $post );
367
368 $diff = time() - strtotime( $post['created'] . ' GMT' );
369 if( $check_permissions && ( ! ( WPF()->perm->forum_can( 'dr', $post['forumid'] ) || ( WPF()->current_userid == $post['userid'] && WPF()->perm->forum_can( 'dor', $post['forumid'] ) ) ) ) ) {
370 WPF()->notice->add( 'You don\'t have permission to delete post from this forum', 'error' );
371
372 return false;
373 }
374
375 if( $check_permissions && ( ! WPF()->perm->forum_can( 'dr', $post['forumid'] ) && wpforo_setting( 'posting', 'delete_own_post_durr' ) !== 0 && $diff > wpforo_setting( 'posting', 'delete_own_post_durr' ) ) ) {
376 WPF()->notice->add( 'The time to delete this post is expired.', 'error' );
377
378 return false;
379 }
380 //Find and delete default attachments before deleting post
381 $this->delete_attachments( $postid );
382
383 //Delete post
384 if( WPF()->db->delete( WPF()->tables->posts, [ 'postid' => $postid ], [ '%d' ] ) ) {
385 $layout = WPF()->forum->get_layout( $post['forumid'] );
386
387 if( isset( $post['parentid'] ) ) {
388 if( ! $post['is_first_post'] && $layout === 4 ) {
389 if( $post['parentid'] == 0 ) {
390 $replies = WPF()->db->get_results( "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `root` = " . wpforo_bigintval( $postid ), ARRAY_A );
391 } else {
392 $children = [];
393 $replies = $this->get_children( $postid, $children, true );
394 }
395 if( ! empty( $replies ) ) {
396 foreach( $replies as $reply ) {
397 if( ! in_array( $reply['postid'], $exclude ) ) {
398 $exclude[] = $reply['postid'];
399 $this->delete( $reply['postid'], false, false, $exclude, false );
400 }
401 }
402 }
403 } elseif( $post['parentid'] != 0 ) {
404 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `parentid` = " . wpforo_bigintval( $post['parentid'] ) . " WHERE `parentid` = " . wpforo_bigintval( $postid ) );
405 }
406 }
407
408 if( $rebuild_data && ! $post['is_first_post'] && $layout === 4 ) WPF()->topic->rebuild_threads( $post['topicid'] );
409
410 do_action( 'wpforo_after_delete_post', $post );
411
412 WPF()->notice->add( 'This post successfully deleted', 'success' );
413
414 if( $post['is_first_post'] ) return WPF()->topic->delete( $post['topicid'], true, $check_permissions );
415 if( $delete_cache ) wpforo_clean_cache( 'post', $postid, $post );
416
417 return true;
418 }
419
420 WPF()->notice->add( 'Post delete error', 'error' );
421 return false;
422 }
423
424 /**
425 * @param int $postid
426 * @param bool $protect
427 *
428 * @return array
429 * @since 1.0.0
430 *
431 */
432 public function _get_post( $postid, $protect = true ) {
433 $sql = "SELECT * FROM `" . WPF()->tables->posts . "` WHERE `postid` = %d";
434 $post = (array) WPF()->db->get_row( WPF()->db->prepare( $sql, $postid ), ARRAY_A );
435
436 if( ! empty( $post ) ) $post['userid'] = wpforo_bigintval( $post['userid'] );
437
438 if( $protect ) {
439 if( isset( $post['forumid'] ) && $post['forumid'] && ! WPF()->perm->forum_can( 'vf', $post['forumid'] ) ) {
440 return [];
441 }
442 if( isset( $post['status'] ) && $post['status'] && ! wpforo_is_owner( $post['userid'], $post['email'] ) ) {
443 if( isset( $post['forumid'] ) && $post['forumid'] && ! WPF()->perm->forum_can( 'au', $post['forumid'] ) ) {
444 return [];
445 }
446 }
447 }
448
449 if ( $post ) {
450 $post['url'] = $this->get_url( $post );
451 $post['full_url'] = $this->get_full_url( $post );
452 $post['short_url'] = $this->get_short_url( $post );
453 }
454
455 return apply_filters( 'wpforo_get_post', $post );
456 }
457
458 public function get_post( $postid, $protect = true ) {
459 return wpforo_ram_get( [ $this, '_get_post' ], $postid, $protect );
460 }
461
462
463 /**
464 * get all posts based on provided arguments
465 *
466 * @param array $args
467 * @param int $items_count
468 * @param bool $count
469 *
470 * @return array
471 * @since 1.0.0
472 *
473 */
474 function get_posts( $args = [], &$items_count = 0, $count = true ) {
475
476 $cache = WPF()->cache->on('post');
477
478 $default = [
479 'include' => [], // array( 2, 10, 25 )
480 'exclude' => [], // array( 2, 10, 25 )
481 'forumids' => [],
482 'topicid' => null, // topic id in DB
483 'forumid' => null, // forum id in DB
484 'parentid' => null, // parent post id
485 'root' => null, // root postid
486 'userid' => null, // user id in DB
487 'orderby' => '`is_first_post` DESC, `created` ASC, `postid` ASC', // forumid, order, parentid
488 'order' => '', // ASC DESC
489 'offset' => null, // this use when you give row_count
490 'row_count' => null, // 4 or 1 ...
491 'status' => null, // 0 or 1 ...
492 'private' => null, // 0 or 1 ...
493 'email' => null, // example@example.com ...
494 'check_private' => true,
495 'where' => null,
496 'owner' => null,
497 'cache_type' => 'sql', // sql or args
498 'limit_per_topic' => null,
499 'union_first_post' => false,
500 'is_first_post' => null,
501 'is_answer' => null,
502 'threaded' => false,
503 ];
504
505 $request = $args;
506 if( empty( $args['orderby'] ) ) $args['order'] = '';
507
508 $args = wpforo_parse_args( $args, $default );
509
510 if( $args['row_count'] === 0 ) return [];
511 if( $args['forumid'] && $args['check_private'] && ! WPF()->perm->forum_can( 'vf', $args['forumid'] ) ) return [];
512 if( strtoupper( (string) $args['order'] ) != 'DESC' && strtoupper( (string) $args['order'] ) != 'ASC' ) $args['order'] = '';
513 if( ! wpforo_root_exist() && ! is_null( $args['root'] ) ) {
514 $args['parentid'] = $args['root'];
515 $args['root'] = null;
516 }
517
518 $wheres = $this->get_posts_conditions( $args );
519
520 $ordering = ( $args['orderby'] ? " ORDER BY " . esc_sql( $args['orderby'] . ' ' . $args['order'] ) : '' );
521 $limiting = ( $args['row_count'] ? " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] ) : '' );
522
523 if( $limit_per_topic = intval( $args['limit_per_topic'] ) ) {
524 $sql = "SELECT SUBSTRING_INDEX( GROUP_CONCAT(`postid` ORDER BY `created` DESC), ',', " . $limit_per_topic . " ) postids
525 FROM `" . WPF()->tables->posts . "` " . ( $wheres ? " WHERE " . implode( " AND ", $wheres ) : '' ) . " GROUP BY `topicid` ORDER BY MAX(`postid`) DESC " . $limiting;
526
527 if( $cache ) {
528 if( $args['cache_type'] === 'sql' ) {
529 $object_key = md5( $sql . WPF()->current_user_groupid );
530 $object_cache = WPF()->cache->get( $object_key );
531 if( ! empty( $object_cache ) ) {
532 return apply_filters( 'wpforo_get_posts', $object_cache['items'] );
533 }
534 }
535 }
536
537 // Returns an array of post IDs ////////////////////////////////
538 $posts = WPF()->db->get_col( $sql );
539 ////////////////////////////////////////////////////////////////
540
541 } else {
542
543 $sql = "SELECT * FROM `" . WPF()->tables->posts . "`";
544 if( ! empty( $wheres ) ) {
545 $sql .= " WHERE " . implode( " AND ", $wheres );
546 }
547 if( $count ) {
548 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql, 1 );
549 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
550 }
551
552 $sql .= $ordering . $limiting;
553
554 if( $args['union_first_post'] && $args['topicid'] && ! $args['parentid'] && $items_count > intval( $args['offset'] ) ) {
555 $sql = "( SELECT * FROM `" . WPF()->tables->posts . "`
556 WHERE `topicid` = " . wpforo_bigintval( $args['topicid'] ) . "
557 AND `is_first_post` = 1 )
558 UNION
559 ( " . $sql . " )";
560 }
561
562 if( $cache ) {
563 if( $args['cache_type'] === 'sql' ) {
564 $object_key = md5( $sql . WPF()->current_user_groupid );
565 $object_cache = WPF()->cache->get( $object_key );
566 if( ! empty( $object_cache ) ) {
567 return apply_filters( 'wpforo_get_posts', $object_cache['items'] );
568 }
569 }
570 if( $args['cache_type'] === 'args' ) {
571 $hach = serialize( $request );
572 $cache_args_key = md5( $hach . WPF()->current_user_groupid );
573 $object_cache = WPF()->cache->get( $cache_args_key, 'loop', 'post' );
574 if( ! empty( $object_cache ) ) {
575 return apply_filters( 'wpforo_get_posts', $object_cache['items'] );
576 }
577 }
578 }
579
580 // Returns an array of posts /////////////////////////////////
581 $posts = WPF()->db->get_results( $sql, ARRAY_A );
582 //////////////////////////////////////////////////////////////
583
584 if( $args['check_private'] && ! $args['forumid'] ) {
585 $posts = $this->access_filter( $posts );
586 }
587 }
588
589 if( $cache && isset( $object_key ) && ! empty( $posts ) ) {
590 self::$cache['posts'][ $object_key ]['items'] = $posts;
591 self::$cache['posts'][ $object_key ]['items_count'] = $items_count;
592 if( isset( $cache_args_key ) && $args['cache_type'] == 'args' ) {
593 WPF()->cache->create_custom( $request, $posts, 'post', $items_count );
594 }
595 }
596
597 return apply_filters( 'wpforo_get_posts', $posts );
598 }
599
600 function get_posts_conditions( $args = [] ) {
601
602 $wheres = [];
603 $table_as_prefix = '`' . WPF()->tables->posts . '`.';
604
605 $args['include'] = wpforo_parse_args( $args['include'] );
606 $args['exclude'] = wpforo_parse_args( $args['exclude'] );
607 $args['forumids'] = wpforo_parse_args( $args['forumids'] );
608
609 if( ! empty( $args['include'] ) ) $wheres[] = $table_as_prefix . "`postid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['include'] ) ) . ")";
610 if( ! empty( $args['exclude'] ) ) $wheres[] = $table_as_prefix . "`postid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['exclude'] ) ) . ")";
611 if( ! empty( $args['forumids'] ) ) $wheres[] = $table_as_prefix . "`forumid` IN(" . implode( ',', array_map( 'intval', $args['forumids'] ) ) . ")";
612
613 if( ! is_null( $args['topicid'] ) ) $wheres[] = $table_as_prefix . "`topicid` = " . wpforo_bigintval( $args['topicid'] );
614 if( ! is_null( $args['parentid'] ) ) $wheres[] = $table_as_prefix . "`parentid` = " . wpforo_bigintval( $args['parentid'] );
615 if( ! is_null( $args['root'] ) ) $wheres[] = $table_as_prefix . "`root` = " . wpforo_bigintval( $args['root'] );
616 if( ! is_null( $args['userid'] ) ) $wheres[] = $table_as_prefix . "`userid` = " . wpforo_bigintval( $args['userid'] );
617 if( ! is_null( $args['status'] ) ) $wheres[] = $table_as_prefix . "`status` = " . intval( (bool) $args['status'] );
618 if( ! is_null( $args['private'] ) ) $wheres[] = $table_as_prefix . "`private` = " . intval( (bool) $args['private'] );
619 if( ! is_null( $args['is_first_post'] ) ) $wheres[] = $table_as_prefix . "`is_first_post` = " . intval( (bool) $args['is_first_post'] );
620 if( ! is_null( $args['is_answer'] ) ) $wheres[] = $table_as_prefix . "`is_answer` = " . intval( (bool) $args['is_answer'] );
621 if( ! is_null( $args['email'] ) ) $wheres[] = $table_as_prefix . "`email` = '" . esc_sql( $args['email'] ) . "' ";
622 if( ! is_null( $args['where'] ) ) $wheres[] = $table_as_prefix . $args['where'];
623
624 if( wpfval( $args, 'forumid' ) && $args['check_private'] ) {
625
626 /////Check "View Reply" Access//////////////////////////////
627 if( ! WPF()->perm->forum_can( 'vr', $args['forumid'] ) ) {
628 $wheres[] = $table_as_prefix . " `is_first_post` = 1";
629 }
630
631 /////Check Unapproved Post Access////////////////////////////
632 if( WPF()->perm->forum_can( 'au', $args['forumid'] ) ) {
633 //Check "Can Approve/Unapprove Posts" Access (View Unapproved Posts)
634 if( ! is_null( $args['status'] ) ) $wheres[] = $table_as_prefix . " `status` = " . intval( $args['status'] );
635 } elseif( WPF()->current_userid ) {
636 //Allow Users see own unapproved posts
637 $wheres[] = " ( " . $table_as_prefix . "`status` = 0 OR (" . $table_as_prefix . "`status` = 1 AND " . $table_as_prefix . "`userid` = " . intval( WPF()->current_userid ) . ") )";
638 } elseif( WPF()->current_user_email ) {
639 //Allow Guests see own unapproved posts
640 $wheres[] = " ( " . $table_as_prefix . "`status` = 0 OR (" . $table_as_prefix . "`status` = 1 AND " . $table_as_prefix . "`email` = '" . sanitize_email( WPF()->current_user_email ) . "') )";
641 } else {
642 //If doesn't have "Can Approve/Unapprove Posts" access and not Owner, only return approved posts
643 $wheres[] = " " . $table_as_prefix . "`status` = 0";
644 }
645 }
646
647 return $wheres;
648 }
649
650 function access_filter( $posts, $user = null ) {
651 if( ! empty( $posts ) ) {
652 foreach( $posts as $key => $post ) {
653 if( ! $this->view_access( $post, $user ) ) unset( $posts[ $key ] );
654 }
655 }
656
657 return $posts;
658 }
659
660 function view_access( $post, $user = null ): bool {
661 $groupids = wpfval( $user, 'groupids' );
662 if(
663 ! WPF()->perm->forum_can( 'vf', $post['forumid'], $groupids )
664 || ! WPF()->perm->forum_can( 'vt', $post['forumid'], $groupids )
665 || ( ! (int) wpfval( $post, 'is_first_post' ) && ! WPF()->perm->forum_can( 'vr', $post['forumid'], $groupids ) )
666 ) return false;
667
668 if( (int) wpfval( $post, 'status' ) && ! WPF()->perm->forum_can( 'au', $post['forumid'], $groupids ) ){
669 $post_owner = wpforo_member( $post );
670 if( ! wpforo_is_users_same( $post_owner, $user ) ) return false;
671 }
672
673 $topic = wpforo_topic( $post['topicid'] );
674 if(
675 (int) wpfval( $topic, 'private' )
676 && ! WPF()->perm->forum_can( 'vp', $post['forumid'], $groupids )
677 ){
678 $topic_owner = wpforo_member( $topic );
679 if( ! wpforo_is_users_same( $topic_owner, $user ) ) return false;
680 }
681
682 return true;
683 }
684
685 function replies( array $posts, $topic = [], $forum = [], $level = 0 ) {
686 $level ++;
687 if( function_exists( 'wpforo_thread_reply' ) ) {
688 if( wpfval( $posts, 'posts' ) ) {
689 $key = key( $posts['posts'] );
690 $parentid = ( wpfval( $posts, 'posts', $key, 'parentid' ) ) ? $posts['posts'][ $key ]['parentid'] : 0;
691 $max_level = wpforo_setting( 'topics', 'layout_threaded_nesting_level' );
692 if( ! $max_level ) {
693 $class = ( $level > 1 ) ? '' : ' level-1';
694 } elseif( $level > ( $max_level + 1 ) ) {
695 $class = '';
696 } else {
697 $class = ' level-' . $level;
698 }
699 echo '<div id="wpf-post-replies-' . intval( $parentid ) . '" class="wpf-post-replies ' . $class . '">';
700 foreach( $posts['posts'] as $post ) {
701 $parents = ( wpfval( $posts, 'parents' ) ) ? $posts['parents'] : [];
702 wpforo_thread_reply( $post, $topic, $forum, $level, $parents );
703 if( ! empty( $post['children'] ) ) {
704 $posts['posts'] = $post['children'];
705 $this->replies( $posts, $topic, $forum, $level );
706 }
707 }
708 echo '</div>';
709 }
710 } else {
711 wpforo_phrase( 'Function wpforo_thread_reply() not found.' );
712 }
713 }
714
715 function get_thread_tree( $post, $parents = true ) {
716 if( ! wpfval( $post, 'postid' ) || ( wpfkey( $post, 'root' ) && $post['root'] == - 1 ) ) {
717 return [ 'posts' => [], 'parents' => [], 'count' => 0, 'children' => '' ];
718 }
719
720 $items = [];
721 $thread = [];
722 $parentid = (int) $post['postid'];
723 $type = apply_filters( 'wpforo_thread_builder_type', 'topic-query' ); //'topic-query', 'inside-mysql', 'multi-query'
724 if( $type === 'topic-query' ) {
725 if( wpfval( $post, 'topicid' ) ) {
726 $args = [ 'root' => $post['postid'], 'orderby' => '`created` ASC' ];
727 $posts = $this->get_posts( $args, $items_count, false );
728 if( empty( $posts ) ) {
729 $args = [ 'topicid' => $post['topicid'], 'orderby' => '`created` ASC' ];
730 $posts = $this->get_posts( $args, $items_count, false );
731 }
732
733 if( ! empty( $posts ) ) {
734 foreach( $posts as $post ) {
735 $items[ $post['postid'] ] = $post;
736 }
737 $thread = $this->build_thread_data( $parentid, $items );
738 }
739 }
740 } elseif( $type === 'inside-mysql' ) {
741 $mod = wpforo_current_user_is( 'admin' ) || wpforo_current_user_is( 'moderator' );
742 if( wpforo_is_db_mysql8() ) {
743 $sql = "WITH RECURSIVE `post_path` AS (
744 SELECT `postid`, `parentid`, `userid`, `status`, `email`
745 FROM `" . WPF()->tables->posts . "`
746 WHERE `parentid` = %d
747 UNION
748 SELECT p.`postid`, p.`parentid`, p.`userid`, p.`status`, p.`email`
749 FROM `" . WPF()->tables->posts . "` p
750 INNER JOIN `post_path` pp ON pp.`parentid` <> pp.`postid` AND pp.`postid` = p.`parentid`
751 ) SELECT CONCAT( `postid`, '-', `parentid`, '-', `userid`, '-', `status`, '-', `email` ) AS tree FROM `post_path`";
752 $posts = WPF()->db->get_col( WPF()->db->prepare( $sql, $parentid ) );
753 } else {
754 $sql = "SELECT GROUP_CONCAT( @id := (
755 SELECT GROUP_CONCAT(postid,'-', parentid, '-', userid, '-', status, '-', email)
756 FROM `" . WPF()->tables->posts . "`
757 WHERE parentid = @id
758 )
759 ) AS tree
760 FROM ( SELECT @id := %d ) vars
761 STRAIGHT_JOIN `" . WPF()->tables->posts . "`
762 WHERE @id IS NOT NULL";
763 $posts = explode( ',', (string) WPF()->db->get_var( WPF()->db->prepare( $sql, $parentid ) ) );
764 }
765 if( ! empty( $posts ) ) {
766 foreach( $posts as $post ) {
767 $post = explode( '-', $post );
768 if( ! $mod && isset( $post[3] ) && $post[3] ) {
769 if( isset( $post[2] ) && isset( $post[4] ) && ( isset( WPF()->current_user['userid'] ) || isset( WPF()->current_user['user_email'] ) ) ) {
770 if( WPF()->current_user['userid'] != $post[2] && WPF()->current_user['user_email'] != $post[4] ) continue;
771 }
772 }
773 if( isset( $post[0] ) && isset( $post[1] ) ) {
774 $items[ $post[0] ] = [ 'postid' => $post[0], 'parentid' => $post[1] ];
775 }
776 }
777 $thread = $this->build_thread_data( $parentid, $items );
778 }
779 } elseif( $type === 'multi-query' ) {
780 $mod = wpforo_current_user_is( 'admin' ) || wpforo_current_user_is( 'moderator' );
781 $items = $this->get_children( $parentid, $children, $mod );
782 $thread = $this->build_thread_data( $parentid, $items );
783 }
784
785 return $thread;
786 }
787
788 function build_thread_data( $parentid, $items = [], $count = 0 ) {
789 $parents = [];
790 $thread = [ 'posts' => [], 'parents' => [], 'count' => 0, 'children' => '' ];
791
792 if( ! empty( $items ) ) {
793 foreach( $items as $item ) {
794 $parents[ $item['postid'] ] = $this->parents( $item['postid'], $items );
795 }
796 if( ! empty( $parents ) ) $thread['parents'] = $parents;
797 $thread['posts'] = $this->build_thread_tree( $items, $parentid );
798 $children = $this->children( $parentid, $thread['posts'] );
799 $thread['count'] = count( $children );
800 $thread['children'] = array_keys( $children );
801 }
802
803 return $thread;
804 }
805
806 function build_thread_tree( array $posts, $parentid = 0 ) {
807 $tree = [];
808 foreach( $posts as $post ) {
809 if( $post['parentid'] == $parentid ) {
810 $children = $this->build_thread_tree( $posts, $post['postid'] );
811 if( $children ) {
812 $post['children'] = $children;
813 }
814 $tree[] = $post;
815 }
816 }
817
818 return $tree;
819 }
820
821 function root( $postid, $parentid = null ) {
822 if( ! $postid ) return 0;
823 $parents = $this->get_parents( $postid, $parentid );
824 $root = array_pop( $parents );
825
826 return intval( $root );
827 }
828
829 function get_root( $postid ) {
830 if( ! $postid || ! wpforo_root_exist() ) return $postid;
831 $root = WPF()->db->get_var( "SELECT `root` FROM `" . WPF()->tables->posts . "` WHERE `postid` = " . intval( $postid ) );
832 if( ! is_null( $root ) && ( $root <= 0 || $root == $postid ) ) {
833 $root = $postid;
834 } else {
835 $root = $this->root( $postid );
836 }
837
838 return $root;
839 }
840
841 function parents( $postid, $posts, $parents = [] ) {
842 if( ! empty( $posts ) ) {
843 if( isset( $posts[ $postid ] ) ) {
844 $parentid = wpfval( $posts[ $postid ], 'parentid' ) ? $posts[ $postid ]['parentid'] : 0;
845 if( $parentid > 0 ) {
846 array_unshift( $parents, $parentid );
847
848 return $this->parents( $parentid, $posts, $parents );
849 }
850 }
851 }
852
853 return $parents;
854 }
855
856 function get_parents( $postid, $parentid = null, &$parents = [], $mod = false ) {
857 if( $postid ) {
858 $status = ( ! $mod ) ? ' AND `status` = 0 ' : '';
859 if( is_null( $parentid ) ) {
860 $where = "`postid` = " . intval( $postid );
861 } else {
862 $where = "`postid` = " . intval( $parentid );
863 }
864 if( $parentid === 0 ) {
865 return $parents;
866 } else {
867 $post = WPF()->db->get_row( "SELECT `postid`, `parentid` FROM `" . WPF()->tables->posts . "` WHERE " . $where . $status, ARRAY_A );
868 if( wpfval( $post, 'parentid' ) ) {
869 $parents[ $post['postid'] ] = $post['parentid'];
870 $this->get_parents( $post['postid'], $post['parentid'], $parents, $mod );
871 }
872 }
873 }
874
875 return $parents;
876 }
877
878 function children( $parentid, $posts, &$children = [] ) {
879 if( $parentid ) {
880 if( ! empty( $posts ) ) {
881 foreach( $posts as $post ) {
882 $children[ $post['postid'] ] = [ 'postid' => $post['postid'], 'parentid' => $post['parentid'] ];
883 if( isset( $post['children'] ) ) $this->children( $post['postid'], $post['children'], $children );
884 }
885 }
886 }
887
888 return $children;
889 }
890
891 function get_children( $parentid, &$children = [], $mod = false ) {
892 if( $parentid ) {
893 $status = ( ! $mod ) ? ' AND `status` = 0 ' : '';
894 $posts = WPF()->db->get_results( "SELECT `postid`, `parentid` FROM `" . WPF()->tables->posts . "` FORCE INDEX (PRIMARY) WHERE `parentid` = " . intval( $parentid ) . " " . $status, ARRAY_A );
895 if( ! empty( $posts ) ) {
896 foreach( $posts as $post ) {
897 $children[ $post['postid'] ] = [ 'postid' => $post['postid'], 'parentid' => $post['parentid'] ];
898 $this->get_children( $post['postid'], $children, $mod );
899 }
900 }
901 }
902
903 return $children;
904 }
905
906 function get_root_replies_count( $postid ) {
907 $postid = intval( $postid );
908 if( $postid && wpforo_root_exist() ) {
909 return (int) WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->posts . "` WHERE `root` = " . $postid );
910 } else {
911 return (int) WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->posts . "` WHERE `parentid` = " . $postid );
912 }
913 }
914
915 function get_posts_filtered( $args = [] ) {
916 $posts = $this->get_posts( $args, $items_count, false );
917 if( ! empty( $posts ) ) {
918 foreach( $posts as $key => $post ) {
919 if( isset( $post['forumid'] ) && ! WPF()->perm->forum_can( 'vf', $post['forumid'] ) ) {
920 unset( $posts[ $key ] );
921 }
922 if( isset( $posts[ $key ] ) && isset( $post['forumid'] ) && isset( $post['private'] ) && $post['private'] && ! wpforo_is_owner( $post['userid'], $post['email'] ) ) {
923 if( ! WPF()->perm->forum_can( 'vp', $post['forumid'] ) ) {
924 unset( $posts[ $key ] );
925 }
926 }
927 if( isset( $posts[ $key ] ) && isset( $post['forumid'] ) && isset( $post['status'] ) && $post['status'] && ! wpforo_is_owner( $post['userid'], $post['email'] ) ) {
928 if( ! WPF()->perm->forum_can( 'au', $post['forumid'] ) ) {
929 unset( $posts[ $key ] );
930 }
931 }
932 }
933 }
934
935 return $posts;
936 }
937
938 function search( $args = [], &$items_count = 0 ) {
939 if( ! is_array( $args ) ) $args = [ 'needle' => $args ];
940 if( ! wpfval( $args, 'needle' ) && ! wpfval( $args, 'postids' ) ) return [];
941
942 $args = array_filter( $args );
943
944 $default = [
945 'needle' => '', // search needle
946 'forumids' => [], // array( 2, 10, 25 )
947 'postids' => [], // array( 2, 10, 25 )
948 'date_period' => 0, // topic id in DB
949 'type' => 'entire-posts', // search type ( entire-posts | titles-only | user-posts | user-topics | tag )
950 'orderby' => 'relevancy', // Sort Search Results by ( relevancy | date | user | forum )
951 'order' => 'DESC', // Sort Search Results ( ASC | DESC )
952 'offset' => null, // this use when you give row_count
953 'row_count' => null // 4 or 1 ...
954 ];
955
956 $args = wpforo_parse_args( $args, $default );
957 $args['postids'] = wpforo_parse_args( $args['postids'] );
958 $args['postids'] = array_filter( array_map( 'wpforo_bigintval', $args['postids'] ) );
959
960 $args['order'] = strtoupper( (string) $args['order'] );
961 if( ! in_array( $args['order'], [ 'ASC', 'DESC' ] ) ) $args['order'] = 'DESC';
962
963 $date_period = intval( $args['date_period'] );
964
965 $fa = "p";
966 $from = "`" . WPF()->tables->posts . "` " . $fa;
967 $selects = [ $fa . '.*' ];
968 $innerjoins = [];
969 $wheres = [];
970 $orders = [];
971
972 if( $args['forumids'] ) $wheres[] = $fa . ".`forumid` IN(" . implode( ', ', array_map( 'intval', $args['forumids'] ) ) . ")";
973 if( $date_period != 0 ) {
974 $date = gmdate( 'Y-m-d H:i:s', time() - ( $date_period * 24 * 60 * 60 ) );
975 if( $date ) $wheres[] = $fa . ".`created` > '" . esc_sql( $date ) . "'";
976 }
977
978 if( $args['needle'] ) {
979 if( in_array( $args['type'], [ 'entire-posts', 'titles-only' ] ) ) {
980 if( apply_filters( 'wpforo_enable_legacy_search', false ) ){
981 $words = preg_split( '#[^\p{L}\p{N}\'`]+#u', $args['needle'] );
982 $words = array_slice( array_filter( $words ), 0, 10 );
983 $words = array_map( function( $w ) {
984 return '+' . esc_sql( str_replace( [ '(', ')', '*', '+', '-', '~', '<', '>', '@', '"' ], '', $w ) ) . '*';
985 }, $words );
986 $needle = implode( ' ', $words );
987 }else{
988 $needle = esc_sql( $args['needle'] );
989 }
990 } else {
991 $needle = esc_sql( $args['needle'] );
992 }
993
994 if( $args['type'] === 'entire-posts' ) {
995 $selects[] = "MATCH(" . $fa . ".`title`) AGAINST('$needle' IN BOOLEAN MODE) + MATCH(" . $fa . ".`body`) AGAINST('$needle' IN BOOLEAN MODE) AS matches";
996 $wheres[] = "( MATCH(" . $fa . ".`title`, " . $fa . ".`body`) AGAINST('$needle' IN BOOLEAN MODE) OR " . $fa . ".`title` LIKE '%" . esc_sql( $args['needle'] ) . "%' OR " . $fa . ".`body` LIKE '%" . esc_sql( $args['needle'] ) . "%' )";
997 $orders[] = "matches";
998 $orders[] = "`created`";
999 } elseif( $args['type'] === 'titles-only' ) {
1000 $selects[] = "MATCH(" . $fa . ".`title`) AGAINST('$needle' IN BOOLEAN MODE) AS matches";
1001 $wheres[] = "( MATCH(" . $fa . ".`title`) AGAINST('$needle' IN BOOLEAN MODE) OR " . $fa . ".`title` LIKE '%" . esc_sql( $args['needle'] ) . "%' )";
1002 $orders[] = "matches";
1003 $orders[] = "`created`";
1004 } elseif( $args['type'] === 'user-posts' || $args['type'] === 'user-topics' ) {
1005 $innerjoins[] = "INNER JOIN `" . WPF()->db->users . "` u ON u.`ID` = " . $fa . ".`userid`";
1006 $wheres[] = "( u.`user_nicename` LIKE '%{$needle}%' OR u.`display_name` LIKE '%{$needle}%' )";
1007 if( $args['type'] === 'user-topics' ) $wheres[] = "" . $fa . ".`is_first_post` = 1";
1008 } elseif( $args['type'] === 'tag' ) {
1009 $fa = "t";
1010 $from = "`" . WPF()->tables->topics . "` " . $fa;
1011 $selects = [ $fa . '.*', $fa . '.`first_postid` AS postid', '1 AS `is_first_post`' ];
1012 $innerjoins = [];
1013 $wheres = [ "( " . $fa . ".`tags` LIKE '%{$needle}%' )" ];
1014 // $wheres = array( "( FIND_IN_SET('{$needle}', ".$fa.".`tags`) )" ); //exact version
1015 }
1016 }
1017
1018 if( $args['postids'] ) $wheres[] = "(" . $fa . ".`postid` IN(" . implode( ',', $args['postids'] ) . "))";
1019
1020 if( $args['orderby'] === 'date' ) {
1021 $orders = [ $fa . '.`created`' ];
1022 } elseif( $args['orderby'] === 'user' ) {
1023 $orders = [ $fa . '.`userid`' ];
1024 } elseif( $args['orderby'] === 'forum' ) {
1025 $orders = [ $fa . '.`forumid`' ];
1026 }
1027
1028 $sql = "SELECT COUNT(*) FROM " . $from . " " . implode( ' ', $innerjoins );
1029 if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
1030 $items_count = (int) WPF()->db->get_var( $sql );
1031 if( wpforo_setting( 'topics', 'search_max_results' ) && $items_count > wpforo_setting( 'topics', 'search_max_results' ) ) $items_count = wpforo_setting( 'topics', 'search_max_results' );
1032
1033 $sql = "SELECT " . implode( ', ', $selects ) . " FROM " . $from . " " . implode( ' ', $innerjoins );
1034 if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
1035 if( $orders ) $sql .= " ORDER BY " . implode( ' ' . $args['order'] . ', ', $orders ) . " " . $args['order'];
1036
1037 if( wpforo_setting( 'topics', 'search_max_results' ) ) $sql = "SELECT * FROM (" . $sql . " LIMIT " . wpforo_setting( 'topics', 'search_max_results' ) . ") AS p";
1038
1039 if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] );
1040
1041 $sql = apply_filters( 'wpforo_search_sql', $sql, $args );
1042
1043 $posts = WPF()->db->get_results( $sql, ARRAY_A );
1044
1045 do_action( 'wpforo_search_result_after', $args, $items_count, $posts, $sql );
1046
1047 foreach( $posts as $key => $post ) {
1048 if( ! WPF()->perm->forum_can( 'vf', $post['forumid'] ) ) unset( $posts[ $key ] );
1049 if( ! WPF()->perm->forum_can( 'vt', $post['forumid'] ) ) unset( $posts[ $key ] );
1050 if( ! $post['is_first_post'] && ! WPF()->perm->forum_can( 'vr', $post['forumid'] ) ) unset( $posts[ $key ] );
1051 if( $post['private'] && ! WPF()->perm->forum_can( 'vp', $post['forumid'] ) ) unset( $posts[ $key ] );
1052 if( $post['status'] && ! WPF()->perm->forum_can( 'au', $post['forumid'] ) ) unset( $posts[ $key ] );
1053 }
1054
1055 return apply_filters('wpforo_post_search', $posts, $args, $items_count);
1056 }
1057
1058 /**
1059 * @param int $postid
1060 *
1061 * @return string
1062 * @since 1.0.0
1063 *
1064 */
1065 public function _get_forumslug_byid( $postid ) {
1066 return (string) WPF()->db->get_var(
1067 WPF()->db->prepare(
1068 "SELECT `slug` FROM " . WPF()->tables->forums . " WHERE `forumid` = (
1069 SELECT forumid FROM `" . WPF()->tables->topics . "` WHERE `topicid` = (
1070 SELECT `topicid` FROM `" . WPF()->tables->posts . "` WHERE postid = %d
1071 )
1072 )",
1073 $postid
1074 )
1075 );
1076 }
1077
1078 public function get_forumslug_byid( $postid ) {
1079 return wpforo_ram_get( [ $this, '_get_forumslug_byid' ], $postid );
1080 }
1081
1082 /**
1083 * @param int
1084 *
1085 * @return string
1086 * @since 1.0.0
1087 *
1088 */
1089 public function _get_topicslug_byid( $postid ) {
1090 return (string) WPF()->db->get_var(
1091 WPF()->db->prepare(
1092 "SELECT `slug` FROM " . WPF()->tables->topics . " WHERE `topicid` = (
1093 SELECT `topicid` FROM `" . WPF()->tables->posts . "` WHERE postid = %d
1094 )",
1095 $postid
1096 )
1097 );
1098 }
1099
1100 public function get_topicslug_byid( $postid ) {
1101 return wpforo_ram_get( [ $this, '_get_topicslug_byid' ], $postid );
1102 }
1103
1104 /**
1105 * @param array $post the array of wpforo post
1106 *
1107 * @return int
1108 */
1109 private function get_position_in_topic( $post ) {
1110 $layout = WPF()->forum->get_layout( $post['forumid'] );
1111 $pid = $post['postid'];
1112 if( $post['parentid'] ) {
1113 switch( $layout ) {
1114 case 3:
1115 $pid = $post['parentid'];
1116 break;
1117 case 4:
1118 $pid = $post['root'];
1119 break;
1120 }
1121 }
1122
1123 $where = "";
1124 $orderby = "`is_first_post` DESC, `created` ASC, `postid` ASC";
1125 if( $layout === 3 ) {
1126 $where .= " AND NOT p.`parentid` ";
1127 $orderby = "`is_first_post` DESC, `is_answer` DESC, `votes` DESC, `created` ASC, `postid` ASC";
1128 } elseif( $layout === 4 ) {
1129 $where .= " AND NOT p.`parentid` ";
1130 }
1131
1132 if( ! wpforo_current_user_is( 'admin' ) && ! wpforo_current_user_is( 'moderator' ) && ! WPF()->perm->forum_can( 'au', $post['forumid'] ) ) {
1133 if( WPF()->current_userid ) {
1134 $where .= " AND ( p.`status` = 0 OR (p.`status` = 1 AND p.`userid` = %d) ) ";
1135 $where = WPF()->db->prepare( $where, WPF()->current_userid );
1136 } elseif( WPF()->current_user_email ) {
1137 $where .= " AND ( p.`status` = 0 OR (p.`status` = 1 AND p.`email` = %s) ) ";
1138 $where = WPF()->db->prepare( $where, sanitize_email( WPF()->current_user_email ) );
1139 } else {
1140 $where .= " AND NOT p.`status` ";
1141 }
1142 }
1143
1144 if( wpforo_is_db_mysql8() ) {
1145 $sql = "SELECT tmp_view.`rownum` FROM
1146 (SELECT ROW_NUMBER() OVER() AS rownum, p.`postid`
1147 FROM `" . WPF()->tables->posts . "` p
1148 WHERE p.`topicid` = %d
1149 " . $where . "
1150 ORDER BY " . $orderby . ") AS tmp_view
1151 WHERE tmp_view.`postid` = %d";
1152 } else {
1153 $sql = "SELECT tmp_view.`rownum` FROM
1154 (SELECT @rownum := @rownum + 1 AS rownum, p.`postid`
1155 FROM `" . WPF()->tables->posts . "` p
1156 CROSS JOIN ( SELECT @rownum := 0 ) AS init_var
1157 WHERE p.`topicid` = %d
1158 " . $where . "
1159 ORDER BY " . $orderby . ") AS tmp_view
1160 WHERE tmp_view.`postid` = %d";
1161 }
1162 $sql = WPF()->db->prepare( $sql, $post['topicid'], $pid );
1163
1164 return (int) WPF()->db->get_var( $sql );
1165 }
1166
1167 /**
1168 * @deprecated since 2.0.10 instead this method use get_url()
1169 *
1170 * @since 1.0.0
1171 *
1172 * @param $arg
1173 *
1174 * @return string
1175 */
1176 function get_post_url( $arg ){
1177 return $this->get_url( $arg );
1178 }
1179
1180 /**
1181 * return post full url by id
1182 *
1183 * @since 2.0.10
1184 *
1185 * @param int|array $arg
1186 *
1187 * @return string $url
1188 */
1189 function get_url( $arg ) {
1190 if( wpforo_setting( 'board', 'url_structure' ) === 'short' ) return $this->get_short_url( $arg );
1191 return $this->get_full_url( $arg );
1192 }
1193
1194 public function get_full_url( $arg ) {
1195 $cache = WPF()->cache->on('url');
1196
1197 if( isset( $arg ) && ! is_array( $arg ) ) {
1198 $postid = wpforo_bigintval( $arg );
1199 $post = $this->get_post( $postid, false );
1200 } elseif( ! empty( $arg ) && isset( $arg['postid'] ) ) {
1201 $post = $arg;
1202 $postid = $post['postid'];
1203 }
1204
1205 if( ! empty( $post ) && is_array( $post ) && ! empty( $postid ) ) {
1206
1207 if( $cache ) {
1208 $post_url = WPF()->cache->get_item( $postid, 'url', 'post' );
1209 if( $post_url ) return $post_url;
1210 }
1211
1212 $forum_slug = ( wpfval( $post, 'forumid' ) ) ? wpforo_forum( $post['forumid'], 'slug' ) : $this->get_forumslug_byid( $postid );
1213 $topic_slug = ( wpfval( $post, 'topicid' ) ) ? wpforo_topic( $post['topicid'], 'slug' ) : $this->get_topicslug_byid( $postid );
1214
1215 $url = $forum_slug . '/' . $topic_slug;
1216
1217 $position = $this->get_position_in_topic( $post );
1218 $items_per_page = $this->get_option_items_per_page( WPF()->forum->get_layout( $post['forumid'] ) );
1219
1220 if( $position <= $items_per_page ) {
1221 $post_url = wpforo_home_url( $url ) . "#post-" . wpforo_bigintval( $postid );
1222 if( $cache ) WPF()->cache->create( 'item', [ 'post_' . intval( $postid ) => $post_url ], 'url' );
1223 return $post_url;
1224 }
1225 if( $position && $items_per_page ) {
1226 $paged = (int) ceil( $position / $items_per_page );
1227 } else {
1228 $paged = 1;
1229 }
1230
1231 $post_url = wpforo_home_url( $url . "/" . wpforo_settings_get_slug( 'paged' ) . "/" . $paged ) . "#post-" . wpforo_bigintval( $postid );
1232 if( $cache ) WPF()->cache->create( 'item', [ 'post_' . intval( $postid ) => $post_url ], 'url' );
1233 return $post_url;
1234 }
1235
1236 return wpforo_home_url();
1237 }
1238
1239 public function get_short_url( $arg ) {
1240 if( is_numeric( $arg ) ){
1241 $postid = $arg;
1242 }elseif( wpfkey( $arg, 'postid' ) ){
1243 $postid = $arg['postid'];
1244 }elseif ( wpfkey( $arg, 'first_postid' ) ){
1245 $postid = $arg['first_postid'];
1246 }else{
1247 $postid = 0;
1248 }
1249
1250 if( $postid = wpforo_bigintval( $postid ) ) return wpforo_home_url( '/' . wpforo_settings_get_slug( 'postid' ) . '/' . $postid . '/' );
1251 return wpforo_home_url();
1252 }
1253
1254 /**
1255 *
1256 * @param int $postid
1257 *
1258 * @return int
1259 * @since 1.0.0
1260 *
1261 */
1262 function is_answered( $postid ) {
1263 $is_answered = WPF()->db->get_var(
1264 WPF()->db->prepare(
1265 "SELECT is_answer FROM `" . WPF()->tables->posts . "` WHERE postid = %d",
1266 $postid
1267 )
1268 );
1269
1270 return $is_answered;
1271 }
1272
1273 function get_best_answer( $topicid, $extended = true ) {
1274 $best_answer = [];
1275 $args = [
1276 'topicid' => $topicid,
1277 'is_answer' => 1,
1278 'status' => 0,
1279 ];
1280 $best_answers = $this->get_posts( $args );
1281 if( ! empty( $best_answers ) && isset( $best_answers[0] ) ) {
1282 $best_answer = $best_answers[0];
1283 } else if( $extended ) {
1284 $best_answer = WPF()->db->get_row(
1285 WPF()->db->prepare(
1286 "SELECT * FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d AND `is_first_post` = 0 AND `parentid` = 0 AND `votes` > 0 ORDER BY `votes` DESC LIMIT 1",
1287 $topicid
1288 ),
1289 ARRAY_A
1290 );
1291 }
1292
1293 return $best_answer;
1294 }
1295
1296 function get_suggested_answers( $topicid, $count = 3 ) {
1297 $args = [
1298 'topicid' => $topicid,
1299 'is_first_post' => 0,
1300 'parentid' => 0,
1301 'is_answer' => 0,
1302 'orderby' => '`votes` DESC, `created` ASC',
1303 'row_count' => $count,
1304 'status' => 0,
1305 ];
1306
1307 return $this->get_posts( $args );
1308 }
1309
1310 function is_approved( $postid ) {
1311 $post = WPF()->db->get_var( "SELECT `status` FROM " . WPF()->tables->posts . " WHERE `postid` = " . intval( $postid ) );
1312 if( $post ) return false;
1313
1314 return true;
1315 }
1316
1317 function get_count( $args = [] ) {
1318 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->posts . "`";
1319 if( $args && is_array( $args ) ) {
1320 $wheres = [];
1321 foreach( $args as $key => $value ) $wheres[] = "`$key` = '" . esc_sql( $value ) . "'";
1322 if( $wheres ) $sql .= " WHERE " . implode( ' AND ', $wheres );
1323 }
1324
1325 return WPF()->db->get_var( $sql );
1326 }
1327
1328 function unapproved_count() {
1329 return (int) WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->posts . "` WHERE `status` = 1" );
1330 }
1331
1332 function get_attachment_id( $filename ) {
1333 $attach_id = WPF()->db->get_var( "SELECT `post_id` FROM `" . WPF()->db->postmeta . "` WHERE `meta_key` = '_wp_attached_file' AND `meta_value` LIKE '%" . esc_sql( $filename ) . "' LIMIT 1" );
1334
1335 return $attach_id;
1336 }
1337
1338 function delete_attachments( $postid ) {
1339 $post = $this->get_post( $postid );
1340 if( isset( $post['body'] ) && $post['body'] ) {
1341 if( preg_match_all( '|\/wpforo\/default_attachments\/([^\s\"\]]+)|i', (string) $post['body'], $attachments, PREG_SET_ORDER ) ) {
1342 foreach( $attachments as $attachment ) {
1343 $filename = trim( $attachment[1] );
1344 $file = WPF()->folders['default_attachments']['dir'] . DIRECTORY_SEPARATOR . $filename;
1345 if( file_exists( $file ) ) {
1346 $posts = WPF()->db->get_var( "SELECT COUNT(*) as posts FROM `" . WPF()->tables->posts . "` WHERE `body` LIKE '%" . esc_sql( $attachment[0] ) . "%'" );
1347 if( is_numeric( $posts ) && $posts == 1 ) {
1348 $attachmentid = $this->get_attachment_id( '/' . $filename );
1349 if( ! wp_delete_attachment( $attachmentid ) ) {
1350 @unlink( $file );
1351 }
1352 }
1353 }
1354 }
1355 }
1356 }
1357 }
1358
1359 public function set_status( $postid, $status ) {
1360 $postid = wpforo_bigintval( $postid );
1361 $status = intval( $status );
1362 if( ! $post = $this->get_post( $postid, false ) ) return false;
1363
1364 if( intval($post['is_first_post']) ) WPF()->topic->set_status( $post['topicid'], $status );
1365
1366 if( $r = WPF()->db->update( WPF()->tables->posts, [ 'status' => $status ], [ 'postid' => $postid ], [ '%d' ], [ '%d' ] ) ) {
1367 if( $status ) {
1368 do_action( 'wpforo_post_unapprove', $post );
1369 } else {
1370 do_action( 'wpforo_post_approve', $post );
1371 }
1372
1373 do_action( 'wpforo_post_status_update', $post, $status );
1374 wpforo_clean_cache( 'post', $postid, $post );
1375 }
1376
1377 if( $r !== false ){
1378 WPF()->notice->add( 'Done!', 'success' );
1379 return true;
1380 }
1381
1382 WPF()->notice->add( 'error: Change Status action', 'error' );
1383 return false;
1384 }
1385
1386 public function next_post( $postid, $topicid = 0 ) {
1387 $next_postid = 0;
1388 if( ! $topicid ) $topicid = wpforo_post( $postid, 'topicid' );
1389 if( $topicid ) $next_postid = WPF()->db->get_var( "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = " . intval( $topicid ) . " AND `postid` > " . intval( $postid ) . " AND `status` = 0 ORDER BY `created` ASC LIMIT 1" );
1390
1391 return intval( $next_postid );
1392 }
1393
1394 public function get_liked_posts( $args, &$items_count ) {
1395 $default = [
1396 'userid' => null,
1397 'order' => 'DESC',
1398 'offset' => null,
1399 'row_count' => null,
1400 'where' => null,
1401 'var' => null,
1402 ];
1403
1404 $posts = [];
1405 if( ! wpfval( $args, 'userid' ) ) return [];
1406 $args = wpforo_parse_args( $args, $default );
1407 if( is_array( $args ) && ! empty( $args ) ) {
1408 extract( $args, EXTR_OVERWRITE );
1409 if( $row_count === 0 ) return [];
1410 $items_count = WPF()->reaction->get_count( ['userid' => $userid, 'type' => 'up'] );
1411 $liked_postids = WPF()->reaction->get_reactions_col( 'postid', ['userid' => $userid, 'type' => 'up', 'orderby' => "`reactionid` $order", 'offset' => $offset, 'row_count' => $row_count] );
1412 if( ! empty( $liked_postids ) ) {
1413 if( $var === 'postid' ) {
1414 return $liked_postids;
1415 } else {
1416 $liked_postids = implode( ',', $liked_postids );
1417 $post_args = [ 'include' => $liked_postids, 'status' => 0, 'private' => 0 ];
1418 $posts = $this->get_posts( $post_args );
1419 }
1420 }
1421 }
1422
1423 return $posts;
1424 }
1425
1426 public function get_unread_posts( $args, $limit = 10 ) {
1427
1428 $unread_posts = [];
1429
1430 //If the unread post logging is disabled return an empty array.
1431 if( ! wpforo_setting( 'logging', 'view_logging' ) ) return $unread_posts;
1432
1433 //If there is no information about last read post.
1434 //Max number recent posts to search unread posts in.
1435 $args['row_count'] = apply_filters( 'wpforo_max_number_of_unread_posts', 100 );
1436
1437 //Find the last unread postid, if so, add 'where' condition.
1438 $last_read_postid = WPF()->log->get_all_read( 'post' );
1439 if( $last_read_postid ) {
1440 $args['where'] = '`postid` > ' . intval( $last_read_postid );
1441 }
1442
1443 //Find unread posts based on last read postid's in topics
1444 $posts = $this->get_posts( $args );
1445 $read_topics = WPF()->log->get_read_topics();
1446 if( ! empty( $posts ) ) {
1447 if( ! empty( $read_topics ) ) {
1448 foreach( $posts as $key => $post ) {
1449 if( $key == $limit ) break;
1450 if( ! wpfkey( $post, 'topicid' ) && $post ) {
1451 $post_ids = explode( ',', $post );
1452 if( ! empty( $post_ids ) ) {
1453 foreach( $post_ids as $post_id ) {
1454 $topicid = wpforo_post( $post_id, 'topicid' );
1455 if( $topicid == wpfval( WPF()->current_object, 'topicid' ) ) continue;
1456 if( wpfkey( $read_topics, $topicid ) ) {
1457 $last_read_postid = $read_topics[ $topicid ];
1458 if( (int) $post_id > (int) $last_read_postid ) {
1459 $unread_posts[] = $post_id;
1460 }
1461 } else {
1462 $unread_posts[] = $post_id;
1463 }
1464 }
1465 }
1466 } elseif( wpfkey( $post, 'topicid' ) && wpfkey( $read_topics, $post['topicid'] ) ) {
1467 $last_read_postid = $read_topics[ $post['topicid'] ];
1468 if( (int) $post['postid'] > (int) $last_read_postid ) {
1469 $unread_posts[] = $post;
1470 }
1471 } else {
1472 $unread_posts[] = $post;
1473 }
1474 }
1475 } else {
1476 $unread_posts = $posts;
1477 }
1478 }
1479
1480 return $unread_posts;
1481 }
1482
1483 public function reset_fields( $type = null ) {
1484 if( is_null( $type ) ) {
1485 $this->fields = [];
1486 } else {
1487 unset( $this->fields, $type );
1488 }
1489 }
1490
1491 private function init_fields( $type, $forum = [] ) {
1492 if( $fields = (array) wpfval( $this->fields, $type ) ) return;
1493 $all_groupids = WPF()->usergroup->get_usergroups( 'groupid' );
1494 $all_groupids = array_map( 'intval', $all_groupids );
1495
1496 $fields = apply_filters( 'wpforo_post_before_init_fields', $fields );
1497
1498 $fields['title'] = [
1499 'fieldKey' => 'title',
1500 'type' => 'text',
1501 'isDefault' => 1,
1502 'isRemovable' => 0,
1503 'isRequired' => 1,
1504 'isEditable' => 1,
1505 'label' => wpforo_phrase( 'Topic Title', false ),
1506 'title' => wpforo_phrase( 'Title', false ),
1507 'placeholder' => wpforo_phrase( 'Enter title here', false ),
1508 'minLength' => 0,
1509 'maxLength' => 0,
1510 'faIcon' => 'fas fa-pen-alt',
1511 'name' => 'title',
1512 'cantBeInactive' => [ 'topic' ],
1513 'canEdit' => $all_groupids,
1514 'canView' => $all_groupids,
1515 'can' => '',
1516 'isSearchable' => 1,
1517 ];
1518
1519 /*$fields['slug'] = array(
1520 'fieldKey' => 'slug',
1521 'type' => 'text',
1522 'isDefault' => 1,
1523 'isRemovable' => 0,
1524 'isRequired' => 0,
1525 'isEditable' => 1,
1526 'label' => wpforo_phrase( 'Slug', false ),
1527 'title' => wpforo_phrase( 'Slug', false ),
1528 'placeholder' => wpforo_phrase( 'Slug', false ),
1529 'minLength' => 0,
1530 'maxLength' => 0,
1531 'faIcon' => 'fas fa-link',
1532 'name' => 'slug',
1533 'cantBeInactive' => array(),
1534 'canEdit' => $all_groupids,
1535 'canView' => $all_groupids,
1536 'can' => '',
1537 'isSearchable' => 1
1538 );*/
1539
1540 $fields['body'] = [
1541 'fieldKey' => 'body',
1542 'type' => 'tinymce',
1543 'isDefault' => 1,
1544 'isRemovable' => 0,
1545 'isRequired' => 1,
1546 'isEditable' => 1,
1547 'title' => wpforo_phrase( 'Body', false ),
1548 'placeholder' => wpforo_phrase( 'Body', false ),
1549 'minLength' => 0,
1550 'maxLength' => 0,
1551 'faIcon' => '',
1552 'name' => 'body',
1553 'cantBeInactive' => [ 'topic', 'post', 'comment', 'reply' ],
1554 'canEdit' => $all_groupids,
1555 'canView' => $all_groupids,
1556 'can' => '',
1557 'isSearchable' => 1,
1558 ];
1559
1560 $fields['name'] = [
1561 'fieldKey' => 'name',
1562 'type' => 'text',
1563 'isDefault' => 1,
1564 'isRemovable' => 0,
1565 'isRequired' => 0,
1566 'isEditable' => 1,
1567 'label' => wpforo_phrase( 'Author Name', false ),
1568 'title' => wpforo_phrase( 'Author Name', false ),
1569 'placeholder' => wpforo_phrase( 'Your name', false ),
1570 'minLength' => 0,
1571 'maxLength' => 0,
1572 'faIcon' => 'fas fa-id-card',
1573 'name' => 'name',
1574 'cantBeInactive' => [],
1575 'canEdit' => $all_groupids,
1576 'canView' => $all_groupids,
1577 'can' => '',
1578 'isSearchable' => 0,
1579 'isOnlyForGuests' => 1,
1580 ];
1581
1582 $fields['email'] = [
1583 'fieldKey' => 'email',
1584 'type' => 'text',
1585 'isDefault' => 1,
1586 'isRemovable' => 0,
1587 'isRequired' => 0,
1588 'isEditable' => 1,
1589 'label' => wpforo_phrase( 'Author Email', false ),
1590 'title' => wpforo_phrase( 'Author Email', false ),
1591 'placeholder' => wpforo_phrase( 'Your email', false ),
1592 'minLength' => 0,
1593 'maxLength' => 0,
1594 'faIcon' => 'fas fa-at',
1595 'name' => 'email',
1596 'cantBeInactive' => [],
1597 'canEdit' => $all_groupids,
1598 'canView' => $all_groupids,
1599 'can' => '',
1600 'isSearchable' => 0,
1601 'isOnlyForGuests' => 1,
1602 ];
1603
1604 /*$fields['tags'] = array(
1605 'fieldKey' => 'tags',
1606 'type' => 'text',
1607 'isDefault' => 1,
1608 'isRemovable' => 0,
1609 'isRequired' => 0,
1610 'isEditable' => 1,
1611 'label' => wpforo_phrase( 'Topic Tags', false ) . ' ' . wpforo_phrase( 'Separate tags using a comma', false ),
1612 'title' => wpforo_phrase( 'Tags', false ),
1613 'placeholder' => wpforo_phrase( 'Start typing tags here (maximum %d tags are allowed)...', false ),
1614 'minLength' => 0,
1615 'maxLength' => 0,
1616 'faIcon' => 'fas fa-tag',
1617 'name' => 'tags',
1618 'cantBeInactive' => array(),
1619 'canEdit' => $all_groupids,
1620 'canView' => $all_groupids,
1621 'can' => '',
1622 'isSearchable' => 1
1623 );
1624
1625 $fields['sticky'] = array(
1626 'fieldKey' => 'sticky',
1627 'type' => 'checkbox',
1628 'isDefault' => 1,
1629 'isRemovable' => 0,
1630 'isRequired' => 0,
1631 'isEditable' => 1,
1632 'label' => wpforo_phrase( 'Set Topic Sticky', false ),
1633 'title' => wpforo_phrase( 'Set Topic Sticky', false ),
1634 'placeholder' => wpforo_phrase( 'Set Topic Sticky', false ),
1635 'faIcon' => 'fas fa-exclamation',
1636 'name' => 'type',
1637 'values' => '1 => ' . wpforo_phrase( 'Set Topic Sticky', false ),
1638 'cantBeInactive' => array(),
1639 'canEdit' => $all_groupids,
1640 'canView' => $all_groupids,
1641 'can' => '',
1642 'isSearchable' => 1
1643 );
1644
1645 $fields['private'] = array(
1646 'fieldKey' => 'private',
1647 'type' => 'checkbox',
1648 'isDefault' => 1,
1649 'isRemovable' => 0,
1650 'isRequired' => 0,
1651 'isEditable' => 1,
1652 'label' => wpforo_phrase( 'Private Topic', false ),
1653 'title' => wpforo_phrase( 'Only Admins and Moderators can see your private topics.', false ),
1654 'placeholder' => wpforo_phrase( 'Private Topic', false ),
1655 'faIcon' => 'fas fa-eye-slash',
1656 'name' => 'private',
1657 'values' => '1 => ' . wpforo_phrase( 'Private Topic', false ),
1658 'cantBeInactive' => array(),
1659 'canEdit' => $all_groupids,
1660 'canView' => $all_groupids,
1661 'can' => '',
1662 'isSearchable' => 1
1663 );
1664
1665 $fields['subscribe'] = array(
1666 'fieldKey' => 'subscribe',
1667 'type' => 'checkbox',
1668 'isDefault' => 1,
1669 'isRemovable' => 0,
1670 'isRequired' => 0,
1671 'isEditable' => 1,
1672 'label' => wpforo_phrase( 'Subscribe to this topic', false ),
1673 'title' => wpforo_phrase( 'Subscribe to this topic', false ),
1674 'placeholder' => wpforo_phrase( 'Subscribe to this topic', false ),
1675 'faIcon' => 'fas fa-eye-slash',
1676 'name' => 'wpforo_topic_subs',
1677 'values' => '1 => ' . wpforo_phrase( 'Subscribe to this topic', false ),
1678 'cantBeInactive' => array(),
1679 'canEdit' => $all_groupids,
1680 'canView' => $all_groupids,
1681 'can' => '',
1682 'isSearchable' => 0
1683 );*/
1684
1685 $fields = (array) apply_filters( 'wpforo_post_after_init_fields', $fields, $type, $forum );
1686
1687 $this->fields[ $type ] = array_map( [ $this, 'fix_field' ], $fields );
1688 }
1689
1690 public function fix_field( $field ) {
1691 $field = array_merge( WPF()->form->default, (array) $field );
1692 if( is_scalar( $field['values'] ) ) $field['values'] = explode( "\n", $field['values'] );
1693
1694 return $field;
1695 }
1696
1697 public function get_fields( $only_defaults = false, $type = 'topic', $forum = [] ) {
1698 $this->init_fields( $type, $forum );
1699 $fields = (array) wpfval( $this->fields, $type );
1700 if( $only_defaults ) foreach( $fields as $k => $v ) if( ! wpfval( $v, 'isDefault' ) ) unset( $fields[ $k ] );
1701
1702 return $fields;
1703 }
1704
1705 public function get_field( $key, $type = 'topic', $forum = [] ) {
1706 if( is_string( $key ) ) {
1707 $this->init_fields( $type, $forum );
1708
1709 return (array) wpfval( $this->fields, $type, $key );
1710 } elseif( $this->is_field( $key ) ) {
1711 return $key;
1712 }
1713
1714 return [];
1715 }
1716
1717 public function is_field( $field ) {
1718 return wpfval( $field, 'fieldKey' ) && wpfval( $field, 'type' ) && wpfkey( $field, 'isDefault' );
1719 }
1720
1721 public function get_field_key( $field ) {
1722 return is_string( $field ) ? $field : (string) wpfval( $field, 'fieldKey' );
1723 }
1724
1725 public function fields_structure_full_array( $fields, &$used_fields = [], $type = 'topic', $forum = [] ) {
1726 if( is_string( $fields ) ) $fields = maybe_unserialize( $fields );
1727 $fs = [ [ [] ] ];
1728 if( ! is_array( $fields ) ) return $fs;
1729 foreach( $fields as $kr => $row ) {
1730 if( is_array( $row ) ) {
1731 foreach( $row as $kc => $cols ) {
1732 if( is_array( $cols ) ) {
1733 foreach( $cols as $kf => $field ) {
1734 $used_fields[] = $field_key = $this->get_field_key( $field );
1735 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field, $type, $forum );
1736 }
1737 }
1738 }
1739 }
1740 }
1741
1742 return $fs;
1743 }
1744
1745 public function strip_guest_fields( $fields, $forum ) {
1746 $topic_fields_list = $this->get_topic_fields_list( false, $forum, true );
1747 if( ! in_array( 'name', $topic_fields_list, true ) ){
1748 foreach ( $fields as $rkey => $row ){
1749 foreach ( $row as $ckey => $col ){
1750 foreach ( $col as $fkey => $field ){
1751 if( $field === 'name' ) unset( $fields[$rkey][$ckey][$fkey] );
1752 }
1753 }
1754 }
1755 }
1756 if( ! in_array( 'email', $topic_fields_list, true ) ){
1757 foreach ( $fields as $rkey => $row ){
1758 foreach ( $row as $ckey => $col ){
1759 foreach ( $col as $fkey => $field ){
1760 if( $field === 'email' ) unset( $fields[$rkey][$ckey][$fkey] );
1761 }
1762 }
1763 }
1764 }
1765
1766 return $fields;
1767 }
1768
1769 // -- START -- get topic fields
1770 public function get_topic_fields_structure( $only_defaults = false, $forum = [], $guest = false ) {
1771 if( $guest ) {
1772 $fields[0][0][0] = 'name';
1773 $fields[0][1][0] = 'email';
1774 }
1775 $fields[][] = [ 'title', 'body' ];
1776 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_topic_fields_structure', $fields, $forum, $guest );
1777
1778 return $fields;
1779 }
1780
1781 public function get_topic_fields( $forum, $values = [], $guest = false ) {
1782 $fields = $this->fields_structure_full_array( $this->get_topic_fields_structure( false, $forum, $guest ), $used_fields, 'topic', $forum );
1783 if( ! in_array( 'title', $used_fields, true ) ) $fields[][][] = $this->get_field( 'title', 'topic', $forum );
1784 if( ! in_array( 'body', $used_fields, true ) ) $fields[][][] = $this->get_field( 'body', 'topic', $forum );
1785
1786 /**
1787 * apply old options to fields
1788 */
1789 $values = wp_slash( $values );
1790 foreach( $fields as $kr => $row ) {
1791 foreach( $row as $kc => $cols ) {
1792 foreach( $cols as $kf => $field ) {
1793 if( $field ) {
1794 $field['value'] = wpfval( $values, $kf );
1795 switch( $kf ) {
1796 case 'body':
1797 if( $field['type'] === 'tinymce' ) {
1798 $field['wp_editor_settings'] = WPF()->tpl->editor_buttons( 'topic' );
1799 }
1800 $field['textareaid'] = uniqid( 'wpf_topic_body_' );
1801 $field['minLength'] = wpforo_setting( 'posting', 'topic_body_min_length' );
1802 $field['maxLength'] = wpforo_setting( 'posting', 'topic_body_max_length' );
1803 $field['form_type'] = 'topic';
1804 $field['meta'] = [ 'forum' => $forum, 'values' => $values ];
1805 break;
1806 case 'title':
1807 if( intval( $forum['layout'] ) === 3 ) $field['label'] = wpforo_phrase( 'Your question', false );
1808 $field['minLength'] = wpforo_setting( 'posting', 'topic_title_min_length' );
1809 $field['maxLength'] = wpforo_setting( 'posting', 'topic_title_max_length' ) ?: 250;
1810 break;
1811 }
1812 if( $field && intval( $field['isOnlyForGuests'] ) || in_array( $kf, [ 'name', 'email' ], true ) ) {
1813 if( $guest ) {
1814 if( ! $values ) {
1815 $g = WPF()->member->get_guest_cookies();
1816 if( $kf === 'name' ) {
1817 $field['value'] = $g['name'];
1818 } elseif( $kf === 'email' ) {
1819 $field['value'] = $g['email'];
1820 }
1821 }
1822 } else {
1823 $field = [];
1824 }
1825 }
1826 $field = apply_filters( 'wpforo_topic_field', $field, $forum, $values, $guest );
1827 }
1828
1829 if( $field ) {
1830 $fields[ $kr ][ $kc ][ $kf ] = $field;
1831 } else {
1832 unset( $fields[ $kr ][ $kc ][ $kf ] );
1833 if( ! $fields[ $kr ][ $kc ] ) {
1834 unset( $fields[ $kr ][ $kc ] );
1835 if( ! $fields[ $kr ] ) unset( $fields[ $kr ] );
1836 }
1837 }
1838
1839 }
1840 }
1841 }
1842
1843 return $fields;
1844 }
1845
1846 public function get_topic_fields_list( $only_defaults = false, $forum = [], $guest = false ) {
1847 $fields_list = [ 'title', 'body' ];
1848 $fields_structure = $this->get_topic_fields_structure( $only_defaults, $forum, $guest );
1849 foreach( $fields_structure as $r ) {
1850 foreach( $r as $c ) {
1851 foreach( $c as $f ) {
1852 $fields_list[] = $f;
1853 }
1854 }
1855 }
1856
1857 return array_values( array_unique( $fields_list ) );
1858 }
1859 // -- END -- get topic fields
1860
1861 // -- START -- get post fields
1862 public function get_post_fields_structure( $only_defaults = false, $forum = [], $guest = false ) {
1863 if( $guest ) {
1864 $fields[0][0][0] = 'name';
1865 $fields[0][1][0] = 'email';
1866 }
1867 $fields[][] = [ 'title', 'body' ];
1868 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_post_fields_structure', $fields, $forum, $guest );
1869
1870 if( $guest ) $fields = $this->strip_guest_fields( $fields, $forum );
1871
1872 return $fields;
1873 }
1874
1875 public function get_post_fields( $forum, $topic, $values = [], $guest = false ) {
1876 $fields = $this->fields_structure_full_array( $this->get_post_fields_structure( false, $forum, $guest ), $used_fields, 'post', $forum );
1877 if( ! in_array( 'body', $used_fields, true ) ) $fields[][][] = $this->get_field( 'body', 'post', $forum );
1878
1879 /**
1880 * apply old options to fields
1881 */
1882 $values = wp_slash( $values );
1883 foreach( $fields as $kr => $row ) {
1884 foreach( $row as $kc => $cols ) {
1885 foreach( $cols as $kf => $field ) {
1886 if( $field ) {
1887 $field['value'] = wpfval( $values, $kf );
1888 switch( $kf ) {
1889 case 'body':
1890 if( $field['type'] === 'tinymce' ) {
1891 $field['wp_editor_settings'] = WPF()->tpl->editor_buttons( 'post' );
1892 }
1893 $field['textareaid'] = uniqid( 'wpf_post_body_' );
1894 $field['minLength'] = wpforo_setting( 'posting', 'post_body_min_length' );
1895 $field['maxLength'] = wpforo_setting( 'posting', 'post_body_max_length' );
1896 $field['form_type'] = 'post';
1897 $field['meta'] = [ 'forum' => $forum, 'topic' => $topic, 'values' => $values ];
1898 break;
1899 case 'title':
1900 $prefix_answer = wpforo_phrase( 'Answer to', false, 'default' );
1901 $prefix_re = wpforo_phrase( 'RE', false, 'default' );
1902 $prefix_patterns = [ $prefix_answer, $prefix_re ];
1903 $pattern = array_map( 'preg_quote', $prefix_patterns );
1904 $pattern = implode( '|', $pattern );
1905 $title = preg_replace( '#^\s*(?:' . $pattern . ')\s*: #isu', '', trim( (string) $field['value'] ), 1 );
1906 if( WPF()->forum->get_layout( $forum ) === 3 ) {
1907 $field['label'] = wpforo_phrase( 'Your question', false );
1908 if( $title ) $field['value'] = $prefix_answer . ': ' . $title;
1909 } else {
1910 $field['label'] = wpforo_phrase( 'Title', false );
1911 if( $title ) $field['value'] = $prefix_re . ': ' . $title;
1912 }
1913 break;
1914 }
1915 if( $field && intval( $field['isOnlyForGuests'] ) || in_array( $kf, [ 'name', 'email' ], true ) ) {
1916 if( $guest ) {
1917 if( ! $values || ( count( $values ) === 1 && wpfkey( $values, 'title' ) ) ) {
1918 $g = WPF()->member->get_guest_cookies();
1919 if( $kf === 'name' ) {
1920 $field['value'] = $g['name'];
1921 } elseif( $kf === 'email' ) {
1922 $field['value'] = $g['email'];
1923 }
1924 }
1925 } else {
1926 $field = [];
1927 }
1928 }
1929 $fields[ $kr ][ $kc ][ $kf ] = apply_filters( 'wpforo_post_field', $field, $forum, $topic, $values, $guest );
1930 }
1931 }
1932 }
1933 }
1934
1935 return $fields;
1936 }
1937
1938 public function get_post_fields_list( $only_defaults = false, $forum = [], $guest = false ) {
1939 $fields_list = [ 'body' ];
1940 $fields_structure = $this->get_post_fields_structure( $only_defaults, $forum, $guest );
1941 foreach( $fields_structure as $r ) {
1942 foreach( $r as $c ) {
1943 foreach( $c as $f ) {
1944 $fields_list[] = $f;
1945 }
1946 }
1947 }
1948
1949 return array_values( array_unique( $fields_list ) );
1950 }
1951
1952 // -- END -- get post fields
1953
1954 public function get_search_fields( $values ) {
1955 $values = (array) $values;
1956 $values = wp_slash( $values );
1957
1958 $topic_fields = WPF()->post->get_topic_fields_list( false, [], ! WPF()->current_userid );
1959 $topic_fields = array_flip( $topic_fields );
1960 $fields = $this->get_fields();
1961 $fields = array_intersect_key( $fields, $topic_fields );
1962
1963 $search_fields = [];
1964 foreach( $fields as $kf => $field ) {
1965 if( ! $field['isDefault'] && (int) wpfval( $field, 'isSearchable' ) && ! (int) wpfval( $field, 'isOnlyForGuests' ) && wpfval( $field, 'type' ) !== 'file' ) {
1966 $field['value'] = wpfval( $values, $kf );
1967 if( in_array( $field['type'], [ 'text', 'textarea', 'email', 'url' ], true ) ) $field['type'] = 'search';
1968 $field['isRequired'] = 0;
1969 $search_fields[0][0][ $field['fieldKey'] ] = $field;
1970 }
1971 }
1972
1973 return $search_fields;
1974 }
1975
1976 public function print_custom_fields( $content, $post ) {
1977 if( (int) wpfval( $post, 'is_first_post' ) && ( $postmetas = WPF()->postmeta->get_postmeta( $post['postid'], '', true ) ) ) {
1978 $forum = WPF()->forum->get_forum( $post['forumid'] );
1979 $fields = WPF()->post->get_topic_fields_list( false, $forum, ! WPF()->current_userid );
1980 $htmls = [ 'before' => [], 'after' => [] ];
1981 foreach( $fields as $field ) {
1982 $display = apply_filters( 'wpforo_topic_fields_filter', true, $field, $post );
1983 if( $display && $postmeta = wpfval( $postmetas, $field ) ) {
1984 $field = WPF()->post->get_field( $field, 'topic', $forum );
1985 if( ! (int) wpfval( $field, 'isDefault' ) ) {
1986 if( (int) $field['isSearchable']
1987 && in_array( $field['type'], ['text','number','select','radio','checkbox','autocomplete'], true )
1988 ){
1989 if( is_scalar( $postmeta ) ){
1990 $postmeta = wpforo_post_search_link( $field['name'], $postmeta );
1991 }else{
1992 $postmeta = array_map( function( $field_value ) use ($field){
1993 return wpforo_post_search_link( $field['name'], $field_value );
1994 }, $postmeta );
1995 }
1996 }
1997 $field['value'] = $postmeta;
1998 $field = WPF()->form->prepare_values( WPF()->form->esc_field( $field ) );
1999 $printTheValue = wpfval( $field, 'printTheValue' ) === 'before' ? 'before' : 'after';
2000 $htmls[$printTheValue][] = sprintf(
2001 '<div class="wpf-topic-field"><div class="wpf-topic-field-label"> <i class="%1$s"></i> %2$s</div><div class="wpf-topic-field-value">%3$s</div></div>',
2002 wpfval( $field, 'faIcon' ),
2003 wpfval( $field, 'label' ),
2004 wpfval( $field, 'value' )
2005 );
2006 }
2007 }
2008 }
2009
2010 $content = sprintf(
2011 '%1$s%2$s%3$s',
2012 ( $htmls['before'] ? sprintf(
2013 '<div class="wpf-topic-fields">%1$s%2$s</div>',
2014 apply_filters( 'wpforo_topic_fields_before', '', $post ),
2015 implode( '', $htmls['before'] )
2016 ) : '' ),
2017 $content,
2018 ( $htmls['after'] ? sprintf(
2019 '<div class="wpf-topic-fields">%1$s%2$s</div>',
2020 apply_filters( 'wpforo_topic_fields_before', '', $post ),
2021 implode( '', $htmls['after'] )
2022 ) : '' )
2023 );
2024 }
2025
2026 return $content;
2027 }
2028
2029 /**
2030 * @param int $postid
2031 * @param int $reaction
2032 * @param int $userid
2033 *
2034 * @return bool
2035 * @since 2.0.0
2036 *
2037 */
2038 public function has_user_voted_post( $postid, $reaction = null, $userid = 0 ) {
2039 $reaction = in_array( $reaction, [1,-1], true ) ? $reaction : null;
2040 $user_post_vote = WPF()->reaction->get_user_reaction_reaction( $postid, $userid );
2041 if( is_null( $reaction ) ) return (bool) $user_post_vote;
2042
2043 return $reaction === $user_post_vote;
2044 }
2045
2046 /**
2047 * @param int $postid
2048 * @param int $reaction
2049 * @param int $userid
2050 *
2051 * @return int
2052 * @since 2.0.0
2053 *
2054 */
2055 public function clear_user_post_votes( $postid, $reaction = null, $userid = 0 ) {
2056 $reaction = in_array( $reaction, [1,-1], true ) ? $reaction : null;
2057 if( ! $userid = wpforo_bigintval( $userid ) ) $userid = WPF()->current_userid;
2058 WPF()->reaction->delete( ['postid' => $postid, 'userid' => $userid, 'reaction_include' => $reaction] );
2059 return WPF()->reaction->get_sum( $postid );
2060 }
2061
2062 /**
2063 * @param int $postid
2064 * @param int $reaction
2065 * @param int $userid
2066 *
2067 * @return false|int
2068 * @since 2.0.0
2069 *
2070 */
2071 public function vote_post( $postid, $reaction, $userid = 0 ) {
2072 if( in_array( ($reaction = intval($reaction)), [1,-1], true ) ) {
2073 $postid = wpforo_bigintval( $postid );
2074 if( ! $userid = wpforo_bigintval( $userid ) ) $userid = WPF()->current_userid;
2075
2076 if( $post = $this->get_post( $postid, false ) ) {
2077 if( false !== WPF()->reaction->add( [ 'userid' => $userid, 'postid' => $postid, 'post_userid' => $post['userid'], 'reaction' => $reaction, 'type' => ( $reaction === 1 ? 'up' : 'down' ) ] ) ) {
2078 $votes = WPF()->reaction->get_sum( $postid );
2079
2080 do_action( 'wpforo_vote', $reaction, $post, $userid );
2081 WPF()->notice->add( 'Successfully voted', 'success' );
2082
2083 return $votes;
2084 }
2085 }
2086 }
2087
2088 return false;
2089 }
2090
2091 /**
2092 * @param int $postid
2093 *
2094 * @return int
2095 * @since 2.0.0
2096 *
2097 */
2098 public function rebuild_votes_stats( $postid ) {
2099 if( $postid = wpforo_bigintval( $postid ) ) {
2100 $votes = WPF()->reaction->get_sum( $postid );
2101
2102 $tu = WPF()->db->update( WPF()->tables->topics, [ 'votes' => $votes ], [ 'first_postid' => $postid ], [ '%d' ], [ '%d' ] );
2103
2104 $pu = WPF()->db->update( WPF()->tables->posts, [ 'votes' => $votes ], [ 'postid' => $postid ], [ '%d' ], [ '%d' ] );
2105
2106 if( $tu || $pu ) wpforo_clean_cache( 'post', $postid );
2107
2108 return $votes;
2109 }
2110
2111 return 0;
2112 }
2113
2114 public function get_userids_for_forum( $forum, $childs = true, $check_permission = true ) {
2115 if( is_numeric( $forum ) ){
2116 // The old and very heavy way to get forum userids, this way does
2117 // lots of recursive SQL queries on each page load. There is no cache for this process.
2118 // The solution has been added in 2.2.4 version by collecting all x participants
2119 // in last_userid field, which were changed from INT to VARCHAR. This solution
2120 // brings userids with forum object without dozens of additional SQLs
2121 if( $childs ) {
2122 $forumids = WPF()->forum->get_forumid_and_childids( $forum, $check_permission );
2123 }else{
2124 $forumids = [ $forum ];
2125 }
2126 $sql = "SELECT DISTINCT `userid` FROM `" . WPF()->tables->posts . "` WHERE `forumid` IN( " . implode( ',', $forumids ) . " ) ORDER BY `created` DESC";
2127 return array_map( 'wpforo_bigintval', WPF()->db->get_col( $sql ) );
2128 }
2129 // The new high performance way to get forum participants
2130 $last_userids = wpfval($forum, 'last_userid');
2131 $last_userids = empty( $last_userids ) ? [] : explode(',', $last_userids);
2132 return array_map( 'wpforo_bigintval', $last_userids );
2133 }
2134
2135 public function get_userids_for_topic( $topicid ) {
2136 $sql = "SELECT DISTINCT `userid`
2137 FROM `" . WPF()->tables->posts . "`
2138 WHERE `topicid` = %d
2139 AND `is_first_post` = 0
2140 ORDER BY `created` DESC";
2141 $sql = WPF()->db->prepare( $sql, $topicid );
2142 return array_map( 'wpforo_bigintval', WPF()->db->get_col( $sql ) );
2143 }
2144
2145 public function get_users_count_for_topic( $topicid ) {
2146 $sql = "SELECT COUNT( DISTINCT `userid` ) AS quantity
2147 FROM `" . WPF()->tables->posts . "`
2148 WHERE `topicid` = %d";
2149 $sql = WPF()->db->prepare( $sql, $topicid );
2150 return (int) WPF()->db->get_var( $sql );
2151 }
2152
2153 public function get_users_stats_for_topic( $topicid, $limit = 0 ) {
2154 $sql = "SELECT `userid`, COUNT(`userid`) AS `posts`
2155 FROM `" . WPF()->tables->posts . "`
2156 WHERE `topicid` = %d
2157 GROUP BY `userid`
2158 ORDER BY `posts` DESC";
2159 $sql = WPF()->db->prepare( $sql, $topicid );
2160 if( $limit = intval( $limit ) ) $sql .= " LIMIT $limit";
2161 return array_map( function( $row ){
2162 $row['userid'] = wpforo_bigintval( $row['userid'] );
2163 $row['posts'] = intval( $row['posts'] );
2164 return $row;
2165 }, (array) WPF()->db->get_results( $sql, ARRAY_A ) );
2166 }
2167
2168 public function get_first_level_replies( $topicid, $row_count = 0, $offset = 0 ) {
2169 $sql = "SELECT *
2170 FROM `" . WPF()->tables->posts . "`
2171 WHERE `topicid` = %d
2172 AND `parentid` = 0
2173 AND `is_first_post` = 0
2174 ORDER BY `created` ASC";
2175 $sql = WPF()->db->prepare( $sql, $topicid );
2176 if( $row_count = intval( $row_count ) ) $sql .= WPF()->db->prepare( " LIMIT %d,%d", $offset, $row_count );
2177 return (array) WPF()->db->get_results( $sql, ARRAY_A );
2178 }
2179
2180 public function after_delete_user( $userid, $reassign ) {
2181 if( $boardids = WPF()->board->get_active_boardids() ){
2182 if( is_null( $reassign ) ) {
2183
2184 foreach( $boardids as $boardid ){
2185 WPF()->change_board( $boardid );
2186 if( $postids = WPF()->db->get_col( WPF()->db->prepare( "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE userid = %d", $userid ) ) ) {
2187 foreach( $postids as $postid ) $this->delete( $postid, true, true, $exclude, false );
2188 }
2189 }
2190
2191 } else {
2192 if( $reassign = wpforo_bigintval( $reassign ) ){
2193 $data = [ 'userid' => $reassign ];
2194 $format = [ '%d' ];
2195 }else{
2196 $data = [
2197 'userid' => 0,
2198 'name' => wpforo_phrase( 'Anonymous', false ) . " " . $userid,
2199 'email' => "anonymous_$userid@example.com",
2200 ];
2201 $format = [ '%d', '%s', '%s' ];
2202 }
2203
2204 foreach( $boardids as $boardid ){
2205 WPF()->change_board( $boardid );
2206 WPF()->db->update( WPF()->tables->posts, $data, [ 'userid' => $userid ], $format, ['%d'] );
2207 }
2208
2209 }
2210 }
2211 }
2212
2213 public function before_delete_reaction( $args, $operator ){
2214 $this->touched_postids = array_merge( $this->touched_postids, WPF()->reaction->_get_reactions_col( 'postid', $args, $operator ) );
2215 }
2216
2217 public function after_delete_reaction(){
2218 foreach( array_unique(array_filter( $this->touched_postids )) as $postid ) $this->rebuild_votes_stats( $postid );
2219 }
2220
2221 public function after_add_reaction( $reaction ) {
2222 $this->rebuild_votes_stats( wpfval( $reaction, 'postid' ) );
2223 }
2224 }
2225