PluginProbe
wpForo Forum / 2.0.9
wpForo Forum v2.0.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 / Topics.php

Topics.php in wpForo Forum 2.0.9, at classes/Topics.php

1,705 lines 59.9 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 Topics {
9 static $cache = [ 'topics' => [], 'tags' => [], 'item' => [], 'topic' => [], 'tag' => [], 'forum_slug' => [] ];
10
11 function __construct() {
12 $this->init_hooks();
13 }
14
15 public function get_cache( $var ) {
16 if( isset( self::$cache[ $var ] ) ) return self::$cache[ $var ];
17
18 return [];
19 }
20
21 public function reset() {
22 self::$cache = [ 'topics' => [], 'tags' => [], 'item' => [], 'topic' => [], 'tag' => [], 'forum_slug' => [] ];
23 }
24
25 private function init_hooks() {
26 add_action( 'wpforo_after_add_post', [ $this, 'after_add_post' ], 10, 2 );
27 add_action( 'wpforo_after_delete_post', [ $this, 'after_delete_post' ] );
28 add_action( 'wpforo_post_status_update', [ $this, 'after_post_status_update' ] );
29 add_action( 'wpforo_after_delete_user', [ $this, 'after_delete_user' ], 11, 2 );
30 }
31
32 private function unique_slug( $slug ) {
33 $new_slug = wpforo_text( $slug, 250, false );
34 $i = 2;
35 while( ! WPF()->can_use_this_slug( $new_slug ) || WPF()->db->get_var( "SELECT `topicid` FROM " . WPF()->tables->topics . " WHERE `slug` = '" . esc_sql( $new_slug ) . "'" ) ) {
36 $new_slug = wpforo_text( $slug, 250, false ) . '-' . $i;
37 $i ++;
38 }
39
40 return $new_slug;
41 }
42
43 public function add( $args = [] ) {
44
45 if( empty( $args ) && empty( $_REQUEST['thread'] ) ) return false;
46 if( empty( $args ) && ! empty( $_REQUEST['thread'] ) ) $args = $_REQUEST['thread'];
47 if( ! isset( $args['body'] ) || ! $args['body'] ) {
48 WPF()->notice->add( 'Post is empty', 'error' );
49
50 return false;
51 }
52 $args['name'] = ( isset( $args['name'] ) ? strip_tags( $args['name'] ) : '' );
53 $args['email'] = ( isset( $args['email'] ) ? sanitize_email( $args['email'] ) : '' );
54
55 if( ! isset( $args['forumid'] ) || ! $args['forumid'] = intval( $args['forumid'] ) ) {
56 WPF()->notice->add( 'Add Topic error: No forum selected', 'error' );
57
58 return false;
59 }
60
61 if( ! $forum = WPF()->forum->get_forum( $args['forumid'] ) ) {
62 WPF()->notice->add( 'Add Topic error: No forum selected', 'error' );
63
64 return false;
65 }
66
67 if( ! WPF()->perm->forum_can( 'ct', $args['forumid'] ) ) {
68 WPF()->notice->add( 'You don\'t have permission to create topic into this forum', 'error' );
69
70 return false;
71 }
72
73 if( ! WPF()->perm->can_post_now() ) {
74 WPF()->notice->add( 'You are posting too quickly. Slow down.', 'error' );
75
76 return false;
77 }
78
79 if( ! isset( $args['title'] ) || ! $args['title'] = trim( strip_tags( $args['title'] ) ) ) {
80 WPF()->notice->add( 'Please insert required fields!', 'error' );
81
82 return false;
83 }
84
85 if( ! is_user_logged_in() ) {
86 if( $args['name'] && $args['email'] ) {
87 WPF()->member->set_guest_cookies( $args );
88 } else {
89 $uqid = uniqid();
90 if( ! trim( $args['name'] ) ) $args['name'] = wpforo_phrase( 'Anonymous', false );
91 if( ! trim( $args['email'] ) ) $args['email'] = "anonymous_$uqid@example.com";
92 }
93 }
94
95 do_action( 'wpforo_start_add_topic', $args, $forum );
96
97 $root_exists = wpforo_root_exist();
98 $args['title'] = wpforo_text( $args['title'], 250, false );
99 $args['body'] = preg_replace( '#</pre>[\r\n\t\s\0]*<pre>#isu', "\r\n", $args['body'] );
100 $args['slug'] = ( isset( $args['slug'] ) && $args['slug'] ) ? sanitize_title( $args['slug'] ) : ( ( isset( $args['title'] ) ) ? sanitize_title( $args['title'] ) : md5( time() ) );
101 if( ! trim( $args['slug'] ) ) $args['slug'] = md5( time() );
102 $args['slug'] = $this->unique_slug( $args['slug'] );
103 $args['created'] = ( isset( $args['created'] ) ? sanitize_text_field( $args['created'] ) : current_time( 'mysql', 1 ) );
104 $args['userid'] = ( isset( $args['userid'] ) ? intval( $args['userid'] ) : WPF()->current_userid );
105 $args['name'] = ( isset( $args['name'] ) ? $args['name'] : '' );
106 $args['email'] = ( isset( $args['email'] ) ? $args['email'] : '' );
107 $args['tags'] = ( isset( $args['tags'] ) ? $args['tags'] : '' );
108
109 $args = apply_filters( 'wpforo_add_topic_data_filter', $args, $forum );
110
111 if( empty( $args ) ) return false;
112
113 extract( $args, EXTR_OVERWRITE );
114
115 if( isset( $title ) ) $title = sanitize_text_field( trim( $title ) );
116 if( isset( $created ) ) $created = sanitize_text_field( $created );
117 if( isset( $userid ) ) $userid = intval( $userid );
118 $type = ( isset( $type ) && $type ? 1 : 0 );
119 $status = ( isset( $status ) && $status ? 1 : 0 );
120 $private = ( isset( $private ) && $private ? 1 : 0 );
121 if( isset( $meta_key ) ) $meta_key = sanitize_text_field( $meta_key );
122 if( isset( $meta_desc ) ) $meta_desc = sanitize_text_field( $meta_desc );
123 if( isset( $name ) ) $name = strip_tags( trim( $name ) );
124 if( isset( $email ) ) $email = strip_tags( trim( $email ) );
125 if( isset( $body ) ) $body = wpforo_kses( trim( $body ) );
126 if( isset( $tags ) ) {
127 if( wpforo_is_module_enabled( 'tags' ) && WPF()->perm->forum_can( 'tag', $forum['forumid'] ) ) {
128 $tags = $this->sanitize_tags( $tags, false, true );
129 } else {
130 $tags = '';
131 }
132 }
133 $views = ( isset( $views ) ? intval( $views ) : 0 );
134 $meta_key = ( isset( $meta_key ) ? $meta_key : '' );
135 $meta_desc = ( isset( $meta_desc ) ? $meta_desc : '' );
136 $has_attach = ( isset( $has_attach ) && $has_attach ) ? 1 : ( ( strpos( $body, '[attach]' ) !== false ) ? 1 : 0 );
137 $layout = WPF()->forum->get_layout( $forum );
138 $posts = ( $layout == 3 ) ? 0 : 1;
139 do_action( 'wpforo_before_add_topic', $args );
140
141 if( WPF()->db->insert(
142 WPF()->tables->topics,
143 [
144 'title' => stripslashes( $title ),
145 'slug' => $slug,
146 'forumid' => $forum['forumid'],
147 'userid' => $userid,
148 'type' => $type,
149 'status' => $status,
150 'private' => $private,
151 'created' => $created,
152 'modified' => $created,
153 'last_post' => 0,
154 'views' => $views,
155 'posts' => $posts,
156 'meta_key' => $meta_key,
157 'meta_desc' => $meta_desc,
158 'has_attach' => $has_attach,
159 'name' => $name,
160 'email' => $email,
161 'tags' => $tags,
162 ],
163 ['%s','%s','%d','%d','%d','%d','%d','%s','%s','%d','%d','%d','%s','%s','%d','%s','%s','%s']
164 ) ) {
165 $topicid = WPF()->db->insert_id;
166 $fields = [
167 'forumid' => $forum['forumid'],
168 'topicid' => $topicid,
169 'userid' => $userid,
170 'title' => stripslashes( $title ),
171 'body' => stripslashes( $body ),
172 'created' => $created,
173 'modified' => $created,
174 'is_first_post' => 1,
175 'status' => $status,
176 'private' => $private,
177 'name' => $name,
178 'email' => $email,
179 'root' => - 1,
180 ];
181
182 $values = [ '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%s', '%s', '%d' ];
183
184 if( ! $root_exists ) {
185 unset( $fields['root'] );
186 unset( $fields[13] );
187 }
188
189 if( WPF()->db->insert(
190 WPF()->tables->posts,
191 $fields,
192 $values
193 ) ) {
194 $first_postid = WPF()->db->insert_id;
195 if( false !== WPF()->db->update( WPF()->tables->topics, [ 'first_postid' => $first_postid, 'last_post' => $first_postid ], [ 'topicid' => $topicid ], [ '%d', '%d' ], [ '%d' ] ) ) {
196 $args['topicid'] = $topicid;
197 $args['first_postid'] = $first_postid;
198 $args['type'] = $type;
199 $args['status'] = $status;
200 $args['private'] = $private;
201 $args['url'] = $args['topicurl'] = $this->get_topic_url( $topicid );
202 if( $tags && ! $status && ! $private ) $this->add_tags( $tags );
203
204 do_action( 'wpforo_after_add_topic', $args, $forum );
205
206 wpforo_clean_cache( 'topic', $topicid, $args );
207 if( $status ) {
208 WPF()->notice->add( 'Your topic successfully added and awaiting moderation', 'success' );
209 } else {
210 WPF()->notice->add( 'Your topic successfully added', 'success' );
211 }
212
213 return $topicid;
214 }
215 }
216
217 }
218
219 WPF()->notice->add( 'Topic add error', 'error' );
220 return false;
221 }
222
223 public function edit( $args = [] ) {
224
225 if( empty( $args ) && empty( $_REQUEST['thread'] ) ) return false;
226 if( ! isset( $args['topicid'] ) && isset( $_GET['id'] ) ) $args['topicid'] = intval( $_GET['id'] );
227 if( empty( $args ) && ! empty( $_REQUEST['thread'] ) ) $args = $_REQUEST['thread'];
228 if( isset( $args['name'] ) ) {
229 $args['name'] = strip_tags( $args['name'] );
230 }
231 if( isset( $args['email'] ) ) {
232 $args['email'] = sanitize_email( $args['email'] );
233 }
234
235
236 if( ! $topic = $this->get_topic( $args['topicid'] ) ) {
237 WPF()->notice->add( 'Topic not found.', 'error' );
238
239 return false;
240 }
241
242 if( ! $forum = WPF()->forum->get_forum( $topic['forumid'] ) ) {
243 WPF()->notice->add( 'Forum not found.', 'error' );
244
245 return false;
246 }
247
248 do_action( 'wpforo_start_edit_topic', $args, $forum );
249
250 if( ! is_user_logged_in() ) {
251 if( ! isset( $topic['email'] ) || ! $topic['email'] ) {
252 WPF()->notice->add( 'Permission denied', 'error' );
253
254 return false;
255 } elseif( ! wpforo_current_guest( $topic['email'] ) ) {
256 WPF()->notice->add( 'You are not allowed to edit this post', 'error' );
257
258 return false;
259 }
260 }
261
262 $args['status'] = $topic['status'];
263 $args['userid'] = $topic['userid'];
264
265 $args = apply_filters( 'wpforo_edit_topic_data_filter', $args, $forum );
266 if( empty( $args ) ) return false;
267
268 extract( $args, EXTR_OVERWRITE );
269
270 if( isset( $topicid ) ) $topicid = intval( $topicid );
271 if( isset( $title ) ) $title = sanitize_text_field( trim( $title ) );
272 if( isset( $type ) ) $type = intval( $type );
273 if( isset( $status ) ) $status = intval( $status );
274 if( isset( $private ) ) $private = intval( $private );
275 if( isset( $name ) ) $name = strip_tags( trim( $name ) );
276 if( isset( $email ) ) $email = strip_tags( trim( $email ) );
277 if( isset( $body ) ) $body = wpforo_kses( trim( $body ) );
278 if( isset( $tags ) ) {
279 if( isset( $topic['forumid'] ) && wpforo_is_module_enabled( 'tags' ) && WPF()->perm->forum_can( 'tag', $topic['forumid'] ) ) {
280 $tags = $this->sanitize_tags( $tags, false, true );
281 } else {
282 $tags = '';
283 }
284 }
285
286
287 if( ! isset( $topicid ) ) {
288 WPF()->notice->add( 'Topic edit error', 'error' );
289
290 return false;
291 }
292 if( ! isset( $title ) || ! $title = trim( strip_tags( $title ) ) ) {
293 WPF()->notice->add( 'Please insert required fields!', 'error' );
294
295 return false;
296 }
297
298 $title = wpforo_text( $title, 250, false );
299 if( isset( $body ) ) $body = preg_replace( '#</pre>[\r\n\t\s\0]*<pre>#isu', "\r\n", $body );
300
301 $diff = current_time( 'timestamp', 1 ) - strtotime( $topic['created'] );
302 if( ! ( WPF()->perm->forum_can( 'et', $topic['forumid'] ) || ( WPF()->current_userid == $topic['userid'] && WPF()->perm->forum_can( 'eot', $topic['forumid'] ) ) ) ) {
303 WPF()->notice->add( 'You have no permission to edit this topic', 'error' );
304
305 return false;
306 }
307
308 if( ! WPF()->perm->forum_can( 'et', $topic['forumid'] ) && wpforo_setting( 'posting', 'edit_own_topic_durr' ) !== 0 && $diff > wpforo_setting( 'posting', 'edit_own_topic_durr' ) ) {
309 WPF()->notice->add( 'The time to edit this topic is expired', 'error' );
310
311 return false;
312 }
313
314 $title = ( isset( $title ) ? stripslashes( $title ) : stripslashes( $topic['title'] ) );
315 $type = ( isset( $type ) ? $type : intval( $topic['type'] ) );
316 $status = ( isset( $status ) ? $status : intval( $topic['status'] ) );
317 $private = ( isset( $private ) ? $private : intval( $topic['private'] ) );
318 $has_attach = ( isset( $body ) ? ( strpos( $body, '[attach]' ) !== false ? 1 : 0 ) : $topic['has_attach'] );
319 $name = ( isset( $name ) ? stripslashes( $name ) : stripslashes( $topic['name'] ) );
320 $email = ( isset( $email ) ? stripslashes( $email ) : stripslashes( $topic['email'] ) );
321 $tags = ( isset( $tags ) ? $tags : '' );
322
323 $t_update = WPF()->db->update( WPF()->tables->topics,
324 [
325 'title' => $title,
326 'type' => $type,
327 'status' => $status,
328 'private' => $private,
329 'has_attach' => $has_attach,
330 'name' => $name,
331 'email' => $email,
332 'tags' => $tags,
333 ],
334 [ 'topicid' => $topicid ],
335 [ '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s' ],
336 [ '%d' ] );
337
338 if( isset( $topic['first_postid'] ) ) {
339 if( ! $post = WPF()->post->get_post( $topic['first_postid'] ) ) {
340 WPF()->notice->add( 'Topic first post data not found.', 'error' );
341
342 return false;
343 }
344 } else {
345 WPF()->notice->add( 'Topic first post not found.', 'error' );
346
347 return false;
348 }
349
350 $body = ( ( isset( $body ) && $body ) ? stripslashes( $body ) : stripslashes( $post['body'] ) );
351
352 $p_update = WPF()->db->update( WPF()->tables->posts,
353 [
354 'title' => $title,
355 'body' => $body,
356 'modified' => current_time(
357 'mysql',
358 1
359 ),
360 'status' => $status,
361 'private' => $private,
362 'name' => $name,
363 'email' => $email,
364 ],
365 [ 'postid' => intval( $topic['first_postid'] ) ],
366 [ '%s', '%s', '%s', '%d', '%d', '%s', '%s' ],
367 [ '%d' ] );
368
369 if( $t_update !== false && $p_update !== false ) {
370
371 if( isset( $tags ) ) $this->edit_tags( $tags, $topic );
372
373 $a = [
374 'userid' => $topic['userid'],
375 'forumid' => $topic['forumid'],
376 'topicid' => $topicid,
377 'postid' => $topic['first_postid'],
378 'first_postid' => $topic['first_postid'],
379 'title' => $title,
380 'body' => $body,
381 'status' => $status,
382 'private' => $private,
383 'name' => $name,
384 'email' => $email,
385 'type' => $type,
386 'has_attach' => $has_attach,
387 'tags' => $tags,
388 ];
389 do_action( 'wpforo_after_edit_topic', $a, $args, $forum );
390
391 wpforo_clean_cache( 'topic-first-post', $topicid, $topic );
392 WPF()->notice->add( 'Topic successfully updated', 'success' );
393
394 return $topicid;
395 }
396
397 WPF()->notice->add( 'Topic edit error', 'error' );
398
399 return false;
400 }
401
402 #################################################################################
403
404 /**
405 * Delete topic from DB
406 *
407 * Returns true if successfully deleted or false.
408 *
409 * @param int $topicid
410 * @param bool $delete_cache
411 *
412 * @return bool
413 * @since 1.0.0
414 *
415 */
416 function delete( $topicid = 0, $delete_cache = true, $check_permissions = true ) {
417 $topicid = intval( $topicid );
418 if( ! $topicid && isset( $_REQUEST['id'] ) ) $topicid = intval( $_REQUEST['id'] );
419
420 if( ! $topic = $this->get_topic( $topicid ) ) return true;
421
422 do_action( 'wpforo_before_delete_topic', $topic );
423
424 if( $check_permissions ){
425 $diff = current_time( 'timestamp', 1 ) - strtotime( $topic['created'] );
426 if( ! ( WPF()->perm->forum_can( 'dt', $topic['forumid'] ) || ( WPF()->current_userid == $topic['userid'] && WPF()->perm->forum_can( 'dot', $topic['forumid'] ) ) ) ) {
427 WPF()->notice->add( 'You don\'t have permission to delete topic from this forum.', 'error' );
428
429 return false;
430 }
431
432 if( ! WPF()->perm->forum_can( 'dt', $topic['forumid'] ) && wpforo_setting( 'posting', 'delete_own_topic_durr' ) !== 0 && $diff > wpforo_setting( 'posting', 'delete_own_topic_durr' ) ) {
433 WPF()->notice->add( 'The time to delete this topic is expired.', 'error' );
434
435 return false;
436 }
437 }
438
439 // START delete topic posts include first post
440 if( $postids = WPF()->db->get_col(
441 WPF()->db->prepare( "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d ORDER BY `is_first_post`", $topicid )
442 ) ) {
443 foreach( $postids as $postid ) {
444 if( $postid == $topic['first_postid'] ) {
445 return WPF()->post->delete( $postid, false, false, $exclude, false );
446 } else {
447 WPF()->post->delete( $postid, false, false, $exclude, false );
448 }
449 }
450 }
451 // END delete topic posts include first post
452
453 if( WPF()->db->delete( WPF()->tables->topics, [ 'topicid' => $topicid ], [ '%d' ] ) ) {
454 if( wpfval( $topic, 'tags' ) ) $this->remove_tags( $topic['tags'] );
455
456 do_action( 'wpforo_after_delete_topic', $topic );
457
458 if( $delete_cache ) wpforo_clean_cache( 'topic', $topicid, $topic );
459 WPF()->notice->add( 'This topic successfully deleted', 'success' );
460 return true;
461 }
462
463 WPF()->notice->add( 'Topics delete error', 'error' );
464
465 return false;
466 }
467
468 #################################################################################
469
470 /**
471 * array get_topic(array or id(num))
472 *
473 * Returns array from defined and default arguments.
474 *
475 * @param mixed $args defined arguments array for returning
476 * @param bool $protect
477 *
478 * @return array
479 * @since 1.0.0
480 *
481 */
482
483 public function _get_topic( $args = [], $protect = true ) {
484 if( ! $args ) return [];
485
486 if( is_array( $args ) ) {
487 $default = [
488 'topicid' => null,
489 'slug' => '',
490 ];
491 } elseif( is_numeric( $args ) ) {
492 $default = [
493 'topicid' => $args,
494 'slug' => '',
495 ];
496 } else {
497 $default = [
498 'topicid' => null,
499 'slug' => $args,
500 ];
501 }
502
503 $args = wpforo_parse_args( $args, $default );
504
505 $sql = "SELECT * FROM `" . WPF()->tables->topics . "`";
506 $wheres = [];
507 if( $topicid = wpforo_bigintval( $args['topicid'] ) ) $wheres[] = "`topicid` = " . $topicid;
508 if( $args['slug'] ) $wheres[] = "`slug` = '" . esc_sql( $args['slug'] ) . "'";
509 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
510
511 $topic = (array) WPF()->db->get_row( $sql, ARRAY_A );
512
513 if( $protect ) {
514 if( isset( $topic['forumid'] ) && $topic['forumid'] && ! WPF()->perm->forum_can( 'vf', $topic['forumid'] ) ) {
515 return [];
516 }
517 if( isset( $topic['private'] ) && $topic['private'] && ! wpforo_is_owner( $topic['userid'], $topic['email'] ) ) {
518 if( isset( $topic['forumid'] ) && $topic['forumid'] && ! WPF()->perm->forum_can( 'vp', $topic['forumid'] ) ) {
519 return [];
520 }
521 }
522 if( isset( $topic['status'] ) && $topic['status'] && ! wpforo_is_owner( $topic['userid'], $topic['email'] ) ) {
523 if( isset( $topic['forumid'] ) && $topic['forumid'] && ! WPF()->perm->forum_can( 'au', $topic['forumid'] ) ) {
524 WPF()->current_object['status'] = 'unapproved';
525
526 return [];
527 }
528 }
529 }
530
531 if( $topic ) $topic['url'] = $this->get_topic_url( $topic, [], false );
532
533 return $topic;
534 }
535
536 public function get_topic( $args = [], $protect = true ) {
537 return wpforo_ram_get( [ $this, '_get_topic' ], $args, $protect );
538 }
539
540 /**
541 * array get_topic(array or id(num))
542 * Returns merged arguments array from defined and default arguments.
543 *
544 * @param array defined arguments array for returning
545 *
546 * @return array where count is topic count and other numeric arrays with topic
547 * @since 1.0.0
548 *
549 */
550 function get_topics( $args = [], &$items_count = 0, $count = true ) {
551 $cache = WPF()->cache->on();
552
553 $default = [
554 'include' => [], // array( 2, 10, 25 )
555 'exclude' => [], // array( 2, 10, 25 )
556 'forumids' => [],
557 'forumid' => null,
558 'userid' => null, // user id in DB
559 'type' => null, //0, 1, etc . . .
560 'solved' => null,
561 'closed' => null,
562 'status' => null, //0, 1, etc . . .
563 'private' => null, //0, 1, etc . . .''
564 'pollid' => null,
565 'orderby' => 'type, topicid', // type, topicid, modified, created
566 'order' => 'DESC', // ASC DESC
567 'offset' => null, // this use when you give row_count
568 'row_count' => null, // 4 or 1 ...
569 'permgroup' => null, //Checks permissions based on attribute value not on current user usergroup
570 'read' => null, //true / false
571 'prefix' => null, //23 / 23,24,50
572 'where' => null,
573 ];
574
575 $args = wpforo_parse_args( $args, $default );
576
577 extract( $args, EXTR_OVERWRITE );
578
579 if( $row_count === 0 ) return [];
580
581 $include = wpforo_parse_args( $include );
582 $exclude = wpforo_parse_args( $exclude );
583 $forumids = wpforo_parse_args( $forumids );
584
585 $guest = [];
586 $wheres = [];
587
588 if( ! is_null( $prefix ) ) {
589 $prefixes = explode( ',', $prefix );
590 if( ! empty( $prefixes ) ) {
591 foreach( $prefixes as $prefixid ) {
592 $wheres[] = " FIND_IN_SET('" . intval( $prefixid ) . "', `prefix`) ";
593 }
594 }
595 }
596
597 if( ! is_null( $read ) ) {
598 $last_read_postid = WPF()->log->get_all_read( 'post' );
599 if( $read ) {
600 if( $last_read_postid ) {
601 $wheres[] = "`last_post` <= " . intval( $last_read_postid );
602 }
603 $include_read = WPF()->log->get_read();
604 $include = array_merge( $include, $include_read );
605 } else {
606 if( $last_read_postid ) {
607 $wheres[] = "`last_post` > " . intval( $last_read_postid );
608 }
609 $exclude_read = WPF()->log->get_read();
610 $exclude = array_merge( $exclude, $exclude_read );
611 }
612 }
613
614 if( ! empty( $include ) ) $wheres[] = "`topicid` IN(" . implode( ', ', array_map( 'intval', $include ) ) . ")";
615 if( ! empty( $exclude ) ) $wheres[] = "`topicid` NOT IN(" . implode( ', ', array_map( 'intval', $exclude ) ) . ")";
616 if( ! empty( $forumids ) ) $wheres[] = "`forumid` IN(" . implode( ', ', array_map( 'intval', $forumids ) ) . ")";
617 if( ! is_null( $forumid ) ) $wheres[] = "`forumid` = " . intval( $forumid );
618 if( ! is_null( $userid ) ) $wheres[] = "`userid` = " . wpforo_bigintval( $userid );
619 if( ! is_null( $solved ) ) $wheres[] = "`solved` = " . intval( $solved );
620 if( ! is_null( $closed ) ) $wheres[] = "`closed` = " . intval( $closed );
621 if( ! is_null( $type ) ) $wheres[] = "`type` = " . intval( $type );
622 if( ! is_null( $where ) ) $wheres[] = $where;
623
624 if( ! is_user_logged_in() ) $guest = WPF()->member->get_guest_cookies();
625
626 if( empty( $forumids ) ) {
627 if( isset( $forumid ) && ! WPF()->perm->forum_can( 'vf', $forumid, $permgroup ) ) {
628 return [];
629 }
630 }
631
632 if( isset( $forumid ) && $forumid ) {
633 if( WPF()->perm->forum_can( 'vp', $forumid, $permgroup ) ) {
634 if( ! is_null( $private ) ) $wheres[] = " `private` = " . intval( $private );
635 } elseif( isset( WPF()->current_userid ) && WPF()->current_userid ) {
636 $wheres[] = " ( `private` = 0 OR (`private` = 1 AND `userid` = " . WPF()->current_userid . ") )";
637 } elseif( wpfval( $guest, 'email' ) ) {
638 $wheres[] = " ( `private` = 0 OR (`private` = 1 AND `email` = '" . sanitize_email( $guest['email'] ) . "') )";
639 } else {
640 $wheres[] = " `private` = 0";
641 }
642 } else {
643 if( ! is_null( $private ) ) $wheres[] = " `private` = " . intval( $private );
644 }
645
646 if( isset( $forumid ) && $forumid ) {
647 if( WPF()->perm->forum_can( 'au', $forumid, $permgroup ) ) {
648 if( ! is_null( $status ) ) $wheres[] = " `status` = " . intval( $status );
649 } elseif( isset( WPF()->current_userid ) && WPF()->current_userid ) {
650 $wheres[] = " ( `status` = 0 OR (`status` = 1 AND `userid` = " . WPF()->current_userid . ") )";
651 } elseif( wpfval( $guest, 'email' ) ) {
652 $wheres[] = " ( `status` = 0 OR (`status` = 1 AND `email` = '" . sanitize_email( $guest['email'] ) . "') )";
653 } else {
654 $wheres[] = " `status` = 0";
655 }
656 } else {
657 if( ! is_null( $status ) ) $wheres[] = " `status` = " . intval( $status );
658 }
659
660 if( function_exists( 'WPF_POLL' ) ) {
661 if( ! is_null( $pollid ) ) $wheres[] = " `pollid` <> 0";
662 }
663
664 $wheres = apply_filters('wpforo_get_topics_sql_wheres', $wheres);
665
666 $sql = "SELECT * FROM `" . WPF()->tables->topics . "`";
667 if( ! empty( $wheres ) ) {
668 $sql .= " WHERE " . implode( " AND ", $wheres );
669 }
670
671 if( $count ) {
672 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
673 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
674 }
675
676 $sql .= " ORDER BY " . str_replace( ',', ' ' . esc_sql( $order ) . ',', esc_sql( $orderby ) ) . " " . esc_sql( $order );
677
678 if( ! is_null( $row_count ) ) {
679 if( ! is_null( $offset ) ) {
680 $sql .= esc_sql( " LIMIT $offset,$row_count" );
681 } else {
682 $sql .= esc_sql( " LIMIT $row_count" );
683 }
684 }
685
686 if( $cache ) {
687 $object_key = md5( $sql . WPF()->current_user_groupid );
688 $object_cache = WPF()->cache->get( $object_key );
689 if( ! empty( $object_cache ) ) {
690 if( ! empty( $object_cache['items'] ) ) {
691 $access_filter = apply_filters( 'wpforo_topic_access_filter_cache', true );
692 if( $access_filter ) {
693 return $this->access_filter( $object_cache['items'], [ 'groupids' => ( array_filter( array_map( 'intval', (array) $permgroup ) ) ?: null )] );
694 } else {
695 return $object_cache['items'];
696 }
697 }
698 }
699 }
700
701 $topics = WPF()->db->get_results( $sql, ARRAY_A );
702 $topics = apply_filters( 'wpforo_get_topics', $topics );
703
704 if( $cache && isset( $object_key ) && ! empty( $topics ) ) {
705 self::$cache['topics'][ $object_key ]['items'] = $topics;
706 self::$cache['topics'][ $object_key ]['items_count'] = $items_count;
707 }
708
709 if( ! empty( $forumids ) || ! $forumid ) {
710 $topics = $this->access_filter( $topics, [ 'groupids' => ( array_filter( array_map( 'intval', (array) $permgroup ) ) ?: null )] );
711 }
712
713 return $topics;
714 }
715
716 function access_filter( $topics, $user = null ) {
717 if( ! empty( $topics ) ) {
718 foreach( $topics as $key => $topic ) {
719 if( ! $this->view_access( $topic, $user ) ) unset( $topics[ $key ] );
720 }
721 }
722
723 return $topics;
724 }
725
726 function view_access( $topic, $user = null ) {
727 $groupids = wpfval( $user, 'groupids' );
728 if( ! WPF()->perm->forum_can( 'vf', $topic['forumid'], $groupids ) ) return apply_filters( 'wpforo_topic_view_access', false, $topic, $user );
729 if( ! WPF()->perm->forum_can( 'vt', $topic['forumid'], $groupids ) ) return apply_filters( 'wpforo_topic_view_access', false, $topic, $user );
730 if(
731 ! wpforo_is_users_same( wpforo_member( $topic ), $user )
732 && (
733 ( (int) wpfval( $topic, 'private' ) && ! WPF()->perm->forum_can( 'vp', $topic['forumid'], $groupids ) )
734 || ( (int) wpfval( $topic, 'status' ) && ! WPF()->perm->forum_can( 'au', $topic['forumid'], $groupids ) )
735 )
736 ) return apply_filters( 'wpforo_topic_view_access', false, $topic, $user );
737
738 return apply_filters( 'wpforo_topic_view_access', true, $topic, $user );
739 }
740
741 /**
742 * Search in your chosen column and return array with needles
743 *
744 * @param string $needle
745 *
746 * @param array $fields can be ( slug, title, body )
747 *
748 * @return array with matches
749 * @since 1.0.0
750 *
751 */
752 function search( $needle = '', $fields = [ 'title', 'body' ] ) {
753 if( $needle !== '' ) {
754 $needle = stripslashes( $needle );
755 $fields = (array) $fields;
756
757 $topicids = [];
758 foreach( $fields as $field ) {
759 if( $field === 'body' ) {
760 // Search the needle as a whole phrase in body to find closer result
761 $matches = WPF()->db->get_col( "SELECT `topicid` FROM " . WPF()->tables->posts . " WHERE `" . esc_sql( $field ) . "` LIKE '%" . esc_sql( sanitize_text_field( $needle ) ) . "%'" );
762 } else {
763 // Search all words of the needle independently in title and other fields
764 // If the search phrase is "es lorem du ipsum dolor", then SQL becomes
765 // SELECT * FROM `wp_wpforo_topics` WHERE `title` REGEXP 'lorem|ipsum|dolor'
766 $words = array_filter( explode(' ', sanitize_text_field( $needle ) ), function($word){ return mb_strlen($word) > 2; });
767 if( !empty( $words ) && apply_filters( 'wpforo_suggested_topics_search_fields_method_regexp', true ) ){
768 $words = array_map( function( $word ){ return esc_sql( $word ); }, $words );
769 $search_mode_sql = 'REGEXP "' . implode('|', $words ) . '"';
770 } else {
771 $search_mode_sql = 'LIKE "%' . esc_sql( sanitize_text_field( $needle ) ) . '%" ';
772 }
773 $matches = WPF()->db->get_col( "SELECT `topicid` FROM " . WPF()->tables->topics . " WHERE `" . esc_sql( $field ) . "` " . $search_mode_sql );
774 }
775 $topicids = array_merge( $topicids, $matches );
776 }
777
778 return array_unique( array_map( 'wpforo_bigintval', $topicids ) );
779 }
780
781 return [];
782 }
783
784 function get_sum_answer( $forumids ) {
785 $sum = WPF()->db->get_var( "SELECT SUM(`answers`) FROM `" . WPF()->tables->topics . "` WHERE `forumid` IN(" . implode( ', ', array_map( 'intval', $forumids ) ) . ")" );
786 if( $sum ) return $sum;
787
788 return 0;
789 }
790
791 function get_forumslug( $forumid ) {
792 $slug = WPF()->db->get_var( "SELECT `slug` FROM " . WPF()->tables->forums . " WHERE `forumid` = " . intval( $forumid ) );
793 if( $slug ) return $slug;
794
795 return 0;
796 }
797
798 function get_forumslug_byid( $topicid ) {
799 $slug = WPF()->db->get_var( "SELECT `slug` FROM " . WPF()->tables->forums . " WHERE `forumid` =(SELECT forumid FROM `" . WPF()->tables->topics . "` WHERE `topicid` =" . intval( $topicid ) . ")" );
800 if( $slug ) return $slug;
801
802 return 0;
803 }
804
805 function is_sticky( $topicid ) {
806 if( $topicid ) {
807 if( WPF()->cache->on() ) {
808 $is_sticky = wpforo_topic( $topicid, 'type' );
809 } else {
810 $sql = "SELECT `type` FROM " . WPF()->tables->topics . " WHERE `topicid` = %d";
811 $is_sticky = WPF()->db->get_var( WPF()->db->prepare( $sql, $topicid ) );
812 }
813
814 return (bool) $is_sticky;
815 }
816
817 return false;
818 }
819
820 function is_private( $topicid ) {
821 if( $topicid ) {
822 if( WPF()->cache->on() ) {
823 $private = wpforo_topic( $topicid, 'private' );
824 } else {
825 $sql = "SELECT `private` FROM " . WPF()->tables->topics . " WHERE `topicid` = %d";
826 $private = WPF()->db->get_var( WPF()->db->prepare( $sql, $topicid ) );
827 }
828
829 return (bool) $private;
830 }
831
832 return false;
833 }
834
835 function is_unapproved( $topicid ) {
836 if( WPF()->cache->on() ) {
837 $status = wpforo_topic( $topicid, 'status' );
838 } else {
839 $status = WPF()->db->get_var( "SELECT `status` FROM " . WPF()->tables->topics . " WHERE `topicid` = " . intval( $topicid ) );
840 }
841 if( $status == 1 ) return true;
842
843 return false;
844 }
845
846 function is_closed( $topicid ) {
847 if( $topicid ) {
848 if( WPF()->cache->on() ) {
849 $closed = wpforo_topic( $topicid, 'closed' );
850 } else {
851 $sql = "SELECT `closed` FROM " . WPF()->tables->topics . " WHERE `topicid` = %d";
852 $closed = WPF()->db->get_var( WPF()->db->prepare( $sql, $topicid ) );
853 }
854
855 return (bool) $closed;
856 }
857
858 return false;
859 }
860
861 function is_solved( $topicid ) {
862 if( $topicid ) {
863 $sql = "SELECT `solved` FROM " . WPF()->tables->topics . " WHERE `topicid` = %d";
864 $solved = WPF()->db->get_var( WPF()->db->prepare( $sql, $topicid ) );
865
866 return (bool) $solved;
867 }
868
869 return false;
870 }
871
872 /**
873 * Move topic to another forum
874 *
875 * @param int $topicid
876 * @param int $forumid
877 *
878 * @return int|false $topicid on success, otherwise false
879 * @since 1.0.0
880 *
881 */
882 function move( $topicid, $forumid ) {
883 $topic = $this->get_topic( $topicid );
884 if( WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `forumid` = " . intval( $forumid ) . " WHERE `topicid` = " . intval( $topicid ) ) ) {
885 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `forumid` = " . intval( $forumid ) . " WHERE `topicid` = " . intval( $topicid ) );
886
887 do_action( 'wpforo_after_move_topic', $topic, $forumid );
888
889 wpforo_clean_cache( 'topic', $topicid, $topic );
890 WPF()->notice->add( 'Done!', 'success' );
891
892 return $topicid;
893 }
894
895 WPF()->notice->add( 'Topic Move Error', 'error' );
896
897 return false;
898 }
899
900 /**
901 * merge topic with target topic
902 *
903 * @param array $target target topic array
904 * @param array $current current topic array
905 * @param array $postids current topic postids array
906 * @param int $to_target_title Update post titles with target topic title
907 * @param int $append Update post dates and append to end
908 *
909 * @return bool true|false true on success, otherwise false
910 */
911 public function merge( $target, $current = [], $postids = [], $to_target_title = 0, $append = 0 ) {
912
913 if( ! $current ) $current = WPF()->current_object['topic'];
914
915 $sql = "UPDATE `" . WPF()->tables->posts . "` SET `topicid` = %d, `forumid` = %d, `private` = %d,`is_first_post` = 0";
916 $sql = WPF()->db->prepare( $sql, $target['topicid'], $target['forumid'], (int) wpfval( $target, 'private' ) );
917
918 if( $append ) {
919 $sql .= ", `modified` = %s, `created` = %s";
920 $sql = WPF()->db->prepare( $sql, current_time( 'mysql', 1 ), current_time( 'mysql', 1 ) );
921 }
922
923 if( $to_target_title ) {
924 $layout = WPF()->forum->get_layout( $target['forumid'] );
925 $phrase = ( $layout == 3 ? wpforo_phrase( 'Answer to', false ) : wpforo_phrase( 'RE', false ) );
926 $title = $phrase . ': ' . $target['title'];
927 $sql .= ", `title` = %s";
928 $sql = WPF()->db->prepare( $sql, $title );
929 }
930
931 $sql .= " WHERE `topicid` = %d";
932 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
933
934 if( $postids ) {
935 $postids = (array) $postids;
936 $postids = array_map( 'wpforo_bigintval', $postids );
937
938 $sql .= " AND `postid` IN(" . implode( ',', $postids ) . ")";
939 }
940
941 do_action( 'wpforo_before_merge_topic', $target, $current, $postids, $to_target_title, $append );
942
943 $db_resp = WPF()->db->query( $sql );
944
945 if( $db_resp !== false ) {
946 $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
947 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
948 if( ! WPF()->db->get_var( $sql ) ) {
949 $this->delete( $current['topicid'], true, false );
950 } else {
951 $this->rebuild_first_last( $current );
952 $this->rebuild_stats( $current );
953 $this->rebuild_threads( $current );
954 wpforo_clean_cache( 'topic', $current['topicid'], $current );
955 }
956
957 $this->rebuild_first_last( $target );
958 $this->rebuild_stats( $target );
959 $this->rebuild_threads( $target );
960
961 do_action( 'wpforo_after_merge_topic', $target, $current, $postids, $to_target_title, $append );
962
963 WPF()->notice->clear();
964 WPF()->notice->add( 'Done!', 'success' );
965
966 wpforo_clean_cache();
967
968 return true;
969 }
970
971 WPF()->notice->add( 'Data merging error', 'error' );
972 return false;
973 }
974
975 public function split( $args, $to_target_title = 0 ) {
976 if( ! $args ) return false;
977
978 $args['name'] = ( isset( $args['name'] ) ? strip_tags( $args['name'] ) : '' );
979 $args['email'] = ( isset( $args['email'] ) ? sanitize_email( $args['email'] ) : '' );
980
981 if( ! isset( $args['forumid'] ) || ! $args['forumid'] = intval( $args['forumid'] ) ) {
982 WPF()->notice->add( 'Please select a target forum', 'error' );
983
984 return false;
985 }
986
987 if( ! isset( $args['title'] ) || ! $args['title'] = trim( strip_tags( $args['title'] ) ) ) {
988 WPF()->notice->add( 'Please insert required fields', 'error' );
989
990 return false;
991 }
992
993 if( empty( $args['postids'] ) ) {
994 WPF()->notice->add( 'Please select at least one post to split', 'error' );
995
996 return false;
997 }
998
999 $args['postids'] = array_values( $args['postids'] );
1000
1001 if( $fpost = WPF()->post->get_post( $args['postids'][0] ) ) {
1002 $args['title'] = wpforo_text( $args['title'], 250, false );
1003 $args['slug'] = ( isset( $args['slug'] ) && $args['slug'] ) ? sanitize_title( $args['slug'] ) : ( ( isset( $args['title'] ) ) ? sanitize_title( $args['title'] ) : md5( time() ) );
1004 if( ! trim( $args['slug'] ) ) $args['slug'] = md5( time() );
1005 $args['slug'] = $this->unique_slug( $args['slug'] );
1006
1007
1008 $args['body'] = $fpost['body'];
1009 $args['created'] = $fpost['created'];
1010 $args['userid'] = $fpost['userid'];
1011 $args['name'] = $fpost['name'];
1012 $args['email'] = $fpost['email'];
1013
1014 extract( $args );
1015
1016 if( isset( $forumid ) ) $forumid = intval( $forumid );
1017 if( isset( $title ) ) $title = sanitize_text_field( trim( $title ) );
1018 if( isset( $slug ) ) $slug = sanitize_title( $slug );
1019 if( isset( $created ) ) $created = sanitize_text_field( $created );
1020 if( isset( $userid ) ) $userid = intval( $userid );
1021 $type = ( isset( $type ) && $type ? 1 : 0 );
1022 $status = ( isset( $status ) && $status ? 1 : 0 );
1023 $private = ( isset( $private ) && $private ? 1 : 0 );
1024 if( isset( $name ) ) $name = strip_tags( trim( $name ) );
1025 if( isset( $email ) ) $email = strip_tags( trim( $email ) );
1026 $has_attach = ( isset( $has_attach ) && $has_attach ) ? 1 : ( ( strpos( $body, '[attach]' ) !== false ) ? 1 : 0 );
1027
1028 if( WPF()->db->insert(
1029 WPF()->tables->topics,
1030 [
1031 'title' => stripslashes(
1032 $title
1033 ),
1034 'slug' => $slug,
1035 'forumid' => $forumid,
1036 'userid' => $userid,
1037 'type' => $type,
1038 'status' => $status,
1039 'private' => $private,
1040 'created' => $created,
1041 'modified' => $created,
1042 'last_post' => 0,
1043 'views' => 0,
1044 'posts' => 1,
1045 'has_attach' => $has_attach,
1046 'name' => $name,
1047 'email' => $email,
1048 ],
1049 [ '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s' ]
1050 ) ) {
1051 $args['topicid'] = $topicid = WPF()->db->insert_id;
1052
1053 if( $this->merge( $args, WPF()->current_object['topic'], $args['postids'], $to_target_title ) ) {
1054 WPF()->notice->clear();
1055 WPF()->notice->add( 'Done!', 'success' );
1056
1057 return $topicid;
1058 }
1059 }
1060 }
1061
1062 WPF()->notice->add( 'Topic splitting error', 'error' );
1063
1064 return false;
1065 }
1066
1067 public function _get_topic_url( $topic, $forum = [], $_cache = true ) {
1068 if( ! is_array( $topic ) ) $topic = $this->get_topic( $topic );
1069 if( $topic ) {
1070 $cache = WPF()->cache->on();
1071 // $_cache is used to stop caching on merge and move actions,
1072 // otherwise the merging and splitting go to redirection loop
1073 if( $_cache && $cache && wpfval($topic, 'topicid') ) {
1074 $topic_url = WPF()->cache->get_item( $topic['topicid'], 'url', 'topic' );
1075 if( $topic_url ) return $topic_url;
1076 }
1077 if( is_array( $forum ) && ! empty( $forum ) ) {
1078 $forum_slug = $forum['slug'];
1079 } else {
1080 $forum_slug = '';
1081 if( wpfval( $topic, 'forumid' ) ) {
1082 $forum_slug = $this->get_forumslug( $topic['forumid'] );
1083 } elseif( wpfval( $topic, 'topicid' ) ) {
1084 $forum_slug = $this->get_forumslug_byid( $topic['topicid'] );
1085 }
1086 }
1087 if( wpfval( $topic, 'slug' ) ) {
1088 $topic_url = wpforo_home_url( $forum_slug . '/' . $topic['slug'] );
1089 if( $cache ) WPF()->cache->create( 'item', [ 'topic_' . intval( $topic['topicid'] ) => $topic_url ], 'url' );
1090 return $topic_url;
1091 }
1092 }
1093
1094 return wpforo_home_url();
1095 }
1096
1097 public function get_topic_url( $topic, $forum = [], $_cache = true ) {
1098 return wpforo_ram_get( [ $this, '_get_topic_url' ], $topic, $forum, $_cache );
1099 }
1100
1101
1102 function get_count( $args = [] ) {
1103 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->topics . "`";
1104 if( ! empty( $args ) ) {
1105 $wheres = [];
1106 foreach( $args as $key => $value ) $wheres[] = "`$key` = '" . esc_sql( $value ) . "'";
1107 if( $wheres ) $sql .= " WHERE " . implode( ' AND ', $wheres );
1108 }
1109
1110 return WPF()->db->get_var( $sql );
1111 }
1112
1113 public function set_status( $topicid, $status ) {
1114 $topicid = wpforo_bigintval( $topicid );
1115 $status = intval( $status );
1116 if( ! $topic = $this->get_topic( $topicid, false ) ) return false;
1117
1118 if( $r = WPF()->db->update( WPF()->tables->topics, [ 'status' => $status ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] ) ) {
1119 if( $status ) {
1120 do_action( 'wpforo_topic_unapprove', $topic );
1121 if( wpfval( $topic, 'tags' ) ) $this->remove_tags( $topic['tags'] );
1122 } else {
1123 do_action( 'wpforo_topic_approve', $topic );
1124 if( wpfval( $topic, 'tags' ) ) $this->add_tags( $topic['tags'] );
1125 }
1126
1127 do_action( 'wpforo_topic_status_update', $topic, $status );
1128 wpforo_clean_cache( 'topic-first-post', $topicid );
1129 }
1130
1131 if( $r !== false ){
1132 WPF()->notice->add( 'Done!', 'success' );
1133 return true;
1134 }
1135
1136 WPF()->notice->add( 'Status changing error', 'error' );
1137 return false;
1138 }
1139
1140 public function wprivate( $topicid, $private ) {
1141 WPF()->db->update( WPF()->tables->topics, [ 'private' => $private ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] );
1142
1143 WPF()->db->update( WPF()->tables->posts, [ 'private' => $private ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] );
1144
1145 do_action( 'wpforo_topic_private_update', $topicid, $private );
1146 wpforo_clean_cache();
1147
1148 return true;
1149 }
1150
1151 public function delete_attachments( $topicid ) {
1152 $args = [ 'topicid' => $topicid ];
1153 $posts = WPF()->post->get_posts( $args );
1154 if( ! empty( $posts ) ) {
1155 foreach( $posts as $post ) {
1156 WPF()->post->delete_attachments( $post['postid'] );
1157 }
1158 }
1159 }
1160
1161 public function rebuild_stats( $topic ) {
1162 if( ! $topic ) return false;
1163 if( is_numeric( $topic ) ) $topic = $this->get_topic( $topic );
1164 if( ! is_array( $topic ) || ! $topic ) return false;
1165
1166 $posts = WPF()->post->get_count( [ 'topicid' => $topic['topicid'], 'status' => 0 ] );
1167
1168 $data = [ 'posts' => $posts, 'answers' => 0 ];
1169 $data_format = [ '%d', '%d' ];
1170
1171 $layout = WPF()->forum->get_layout( $topic['forumid'] );
1172 if( $layout === 3 ) {
1173 $data['answers'] = WPF()->post->get_count( [ 'topicid' => $topic['topicid'], 'status' => 0, 'parentid' => 0, 'is_first_post' => 0 ] );
1174 $data['posts'] = $posts - 1;
1175 }
1176
1177 if( $r = WPF()->db->update( WPF()->tables->topics, $data, [ 'topicid' => $topic['topicid'] ], $data_format, [ '%d' ] ) ) {
1178 wpforo_clean_cache( 'topic-first-post', $topic['topicid'], $topic );
1179 }
1180
1181 return $r !== false;
1182 }
1183
1184 public function rebuild_first_last( $topic ) {
1185 if( ! $topic ) return false;
1186 if( is_numeric( $topic ) ) $topic = $this->get_topic( $topic );
1187 if( ! is_array( $topic ) || ! $topic ) return false;
1188
1189 $sql = "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d ORDER BY `is_first_post` DESC, `created` ASC, `postid` ASC LIMIT 1";
1190 if( $first_postid = WPF()->db->get_var( WPF()->db->prepare( $sql, $topic['topicid'] ) ) ) {
1191 $sql = "UPDATE `" . WPF()->tables->posts . "` SET `is_first_post` = 1 WHERE `postid` = %d";
1192 WPF()->db->query( WPF()->db->prepare( $sql, $first_postid ) );
1193
1194 do_action( 'wpforo_after_is_first_post_update', $first_postid, 1 );
1195 } else {
1196 $first_postid = 0;
1197 }
1198
1199 $sql = "SELECT `postid`, `created`
1200 FROM `" . WPF()->tables->posts . "`
1201 WHERE `topicid` = %d
1202 ORDER BY `is_first_post` ASC, `created` DESC, `postid` DESC LIMIT 1";
1203 if( ! $last_post = WPF()->db->get_row( WPF()->db->prepare( $sql, $topic['topicid'] ), ARRAY_A ) ) {
1204 $last_post = [ 'postid' => 0, 'created' => $topic['modified'] ];
1205 }
1206
1207 if( $r = WPF()->db->update(
1208 WPF()->tables->topics,
1209 [
1210 'first_postid' => $first_postid,
1211 'last_post' => $last_post['postid'],
1212 'modified' => $last_post['created'],
1213 ],
1214 [ 'topicid' => $topic['topicid'] ],
1215 [ '%d', '%d', '%s' ],
1216 [ '%d' ]
1217 ) ) {
1218 wpforo_clean_cache( 'topic-first-post' );
1219 }
1220
1221 return $r !== false;
1222 }
1223
1224 function rebuild_threads( $topicid, $root = null ) {
1225 if( ! is_null( $root ) && $root <= 0 ) return;
1226 if( is_array( $topicid ) && wpfval( $topicid, 'topicid' ) ) $topicid = $topicid['topicid'];
1227 if( $topicid ) {
1228 $posts = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->posts . "` WHERE `topicid` = " . intval( $topicid ), ARRAY_A );
1229 if( ! empty( $posts ) ) {
1230 $threads = [];
1231 foreach( $posts as $post ) {
1232 $threads[ $post['postid'] ] = [ 'postid' => $post['postid'], 'parentid' => $post['parentid'] ];
1233 }
1234 unset( $posts );
1235 foreach( $threads as $item ) {
1236 if( ! is_null( $root ) && $root != $item['postid'] ) continue;
1237 if( ! $item['parentid'] ) {
1238 $thread = WPF()->post->build_thread_data( $item['postid'], $threads );
1239 if( wpfval( $thread, 'children' ) ) {
1240 $children = implode( ',', $thread['children'] );
1241 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = 0 WHERE `postid` = " . intval( $item['postid'] ) );
1242 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = " . intval( $item['postid'] ) . " WHERE `postid` IN(" . esc_sql( $children ) . ")" );
1243 }
1244 } elseif( ! wpfkey( $threads, $item['parentid'] ) ) {
1245 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = 0, `parentid` = 0 WHERE `postid` = " . intval( $item['postid'] ) );
1246 }
1247 }
1248 }
1249 }
1250 }
1251
1252 function rebuild_forum_threads( $forumid = 0 ) {
1253 if( ! $forumid ) {
1254 $args = [ 'layout' => 4 ];
1255 $forums = WPF()->forum->get_forums( $args );
1256 if( ! empty( $forums ) ) {
1257 foreach( $forums as $forum ) {
1258 $args = [ 'forumid' => $forum['forumid'] ];
1259 $topics = $this->get_topics( $args );
1260 if( ! empty( $topics ) ) {
1261 foreach( $topics as $topic ) {
1262 $this->rebuild_threads( $topic['topicid'] );
1263 }
1264 }
1265 }
1266 }
1267 } else {
1268 $args = [ 'forumid' => $forumid ];
1269 $topics = $this->get_topics( $args );
1270 if( ! empty( $topics ) ) {
1271 foreach( $topics as $topic ) {
1272 $this->rebuild_threads( $topic['topicid'] );
1273 }
1274 }
1275 }
1276 }
1277
1278 public function members( $topicid, $limit = 0 ) {
1279 if( ! $topicid ) return [];
1280 $members = [];
1281 $args = [
1282 'topicid' => $topicid,
1283 'orderby' => 'created',
1284 'order' => 'ASC',
1285 'private' => 0,
1286 'status' => 0,
1287 'cache_type' => 'args',
1288 ];
1289 $posts = WPF()->post->get_posts( $args );
1290 foreach( $posts as $post ) {
1291 if( wpfval( $post, 'userid' ) ) {
1292 $members[ $post['userid'] ] = wpforo_member( $post['userid'] );
1293 if( $limit && count( $members ) >= $limit ) break;
1294 }
1295 }
1296
1297 return array_filter( $members );
1298 }
1299
1300 public function can_answer( $topicid ) {
1301 if( ! $topicid ) return false;
1302 $topic = wpforo_topic( $topicid );
1303 if( wpfval( $topic, 'topicid' ) ) {
1304 if( wpfval( $topic, 'userid' ) ) {
1305 if( ! WPF()->perm->forum_can( 'aot', $topic['forumid'] ) && WPF()->current_userid == $topic['userid'] ) {
1306 return false;
1307 }
1308 } else {
1309 $guest = WPF()->member->get_guest_cookies();
1310 if( wpfval( $topic, 'email' ) && wpfval( $guest, 'email' ) ) {
1311 if( ! WPF()->perm->forum_can( 'aot', $topic['forumid'] ) && $topic['email'] == $guest['email'] ) {
1312 return false;
1313 }
1314 }
1315 }
1316 }
1317
1318 return true;
1319 }
1320
1321 /**
1322 * @param $topicid
1323 *
1324 * @return bool
1325 */
1326 public function has_is_answer_post( $topicid ) {
1327 if( ! $topicid ) return false;
1328 $sql = "SELECT EXISTS ( SELECT * FROM `" . WPF()->tables->posts . "` WHERE `is_answer` = 1 AND `topicid` = %d) AS is_exists";
1329 $sql = WPF()->db->prepare( $sql, $topicid );
1330
1331 return (bool) WPF()->db->get_var( $sql );
1332 }
1333
1334 public function get_postids( $topicid ) {
1335 $sql = "SELECT `postid`
1336 FROM `" . WPF()->tables->posts . "`
1337 WHERE `topicid` = %d";
1338 $sql = WPF()->db->prepare( $sql, $topicid );
1339 return array_map( 'wpforo_bigintval', WPF()->db->get_col( $sql ) );
1340 }
1341
1342 public function add_tags( $tags ) {
1343 if( $tags ) {
1344 $tags = $this->sanitize_tags( $tags, true );
1345 if( ! empty( $tags ) ) {
1346 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1347 foreach( $tags as $tag ) {
1348 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1349 if( $count ) {
1350 WPF()->db->update( WPF()->tables->tags, [ 'count' => $count + 1 ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1351 } else {
1352 $this->add_tag( $tag, 1 );
1353 }
1354 wpforo_clean_cache( 'tag', $tag );
1355 }
1356 wpforo_clean_cache( 'tag' );
1357 }
1358 }
1359 }
1360
1361 public function add_tag( $tag, $count = 0 ) {
1362 $tagid = 0;
1363 if( $tag ) {
1364 $tag = $this->sanitize_tag( $tag );
1365 if( ! WPF()->db->get_var( "SELECT `tagid` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" ) ) {
1366 $tagid = WPF()->db->insert( WPF()->tables->tags, [ 'tag' => $tag, 'prefix' => 0, 'count' => intval( $count ) ], [ '%s', '%d', '%d' ] );
1367 }
1368 }
1369
1370 return $tagid;
1371 }
1372
1373 public function edit_tags( $tags, $topic = [] ) {
1374 $old_tags = ( wpfval( $topic, 'tags' ) ) ? $this->sanitize_tags( $topic['tags'], true ) : false;
1375 if( $tags ) {
1376 $tags = $this->sanitize_tags( $tags, true );
1377 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1378 if( ! empty( $tags ) ) {
1379 if( wpfval( $topic, 'topicid' ) ) {
1380 if( ! empty( $old_tags ) ) {
1381 foreach( $old_tags as $old_tag ) {
1382 if( ! in_array( $old_tag, $tags ) ) {
1383 $this->remove_tags( $old_tag );
1384 }
1385 }
1386 }
1387 }
1388 if( ! wpfval( $topic, 'status' ) && ! wpfval( $topic, 'private' ) ) {
1389 foreach( $tags as $tag ) {
1390 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1391 if( ! $count ) {
1392 WPF()->db->insert( WPF()->tables->tags, [ 'tag' => $tag, 'prefix' => 0, 'count' => 1 ], [ '%s', '%d', '%d' ] );
1393 } elseif( empty( $old_tags ) || ! in_array( $tag, $old_tags ) ) {
1394 WPF()->db->update( WPF()->tables->tags, [ 'count' => ( $count + 1 ) ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1395 }
1396 wpforo_clean_cache( 'tag', $tag );
1397 }
1398 }
1399 }
1400 } else {
1401 if( ! empty( $old_tags ) && wpfval( $topic, 'topicid' ) ) {
1402 WPF()->db->update( WPF()->tables->topics, [ 'tags' => '' ], [ 'topicid' => $topic['topicid'] ], [ '%s' ], [ '%d' ] );
1403 $this->remove_tags( $old_tags );
1404 }
1405 }
1406 wpforo_clean_cache( 'tag' );
1407 }
1408
1409 public function edit_tag( $tag, $old_tag = '', $disconnect = false ) {
1410
1411 $tag_id = wpfval( $tag, 'tagid' );
1412 $tag_name = wpfval( $tag, 'tag' );
1413 $tag_name = $this->sanitize_tag( $tag_name );
1414
1415 if( false !== WPF()->db->update( WPF()->tables->tags, [ 'tag' => $tag_name ], [ 'tagid' => intval( $tag_id ) ], [ '%s' ], [ '%d' ] ) ) {
1416 if( $old_tag ) {
1417 if( $disconnect ) {
1418 $this->remove_tag_from_topics( $old_tag );
1419 WPF()->db->update( WPF()->tables->tags, [ 'count' => 0 ], [ 'tag' => $tag_name ], [ '%d' ], [ '%s' ] );
1420 } else {
1421 if( $old_tag != $tag_name ) {
1422 $this->update_tag_in_topics( $tag_name, $old_tag );
1423 }
1424 $count = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->topics . "` WHERE FIND_IN_SET('" . esc_sql( $tag_name ) . "', tags)" );
1425 WPF()->db->update( WPF()->tables->tags, [ 'count' => intval( $count ) ], [ 'tag' => $tag_name ], [ '%d' ], [ '%s' ] );
1426 }
1427 }
1428 wpforo_clean_cache( 'tag', $tag_name );
1429
1430 return true;
1431 }
1432
1433 return false;
1434 }
1435
1436 public function remove_tags( $tags ) {
1437 if( $tags ) {
1438 $tags = $this->sanitize_tags( $tags, true );
1439 foreach( $tags as $tag ) {
1440 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1441 if( $count === 1 ) {
1442 WPF()->db->query( "DELETE FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1443 } else {
1444 WPF()->db->update( WPF()->tables->tags, [ 'count' => ( $count - 1 ) ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1445 }
1446 wpforo_clean_cache( 'tag', $tag );
1447 }
1448 }
1449 wpforo_clean_cache( 'tag' );
1450 }
1451
1452 public function remove_tag( $tag ) {
1453 if( $tag ) {
1454 $this->remove_tag_from_topics( $tag );
1455 $sql_delete_tag = "DELETE FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'";
1456 WPF()->db->query( $sql_delete_tag );
1457 wpforo_clean_cache( 'tag', $tag );
1458 }
1459 }
1460
1461 public function remove_tag_from_topics( $tag ) {
1462 if( $tag ) {
1463 WPF()->db->query(
1464 "UPDATE `" . WPF()->tables->topics . "`
1465 SET tags = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', tags, ','), '," . esc_sql( $tag ) . ",', ','))
1466 WHERE FIND_IN_SET('" . esc_sql( $tag ) . "', tags)"
1467 );
1468 }
1469 }
1470
1471 public function update_tag_in_topics( $new_tag, $old_tag ) {
1472 if( $new_tag && $old_tag ) {
1473
1474 WPF()->db->query(
1475 "UPDATE `" . WPF()->tables->topics . "`
1476 SET tags = '" . esc_sql( $new_tag ) . "'
1477 WHERE tags LIKE '" . esc_sql( $old_tag ) . "'"
1478 );
1479
1480 WPF()->db->query(
1481 "UPDATE `" . WPF()->tables->topics . "`
1482 SET tags = REPLACE(tags, '," . esc_sql( $old_tag ) . ",', '," . esc_sql( $new_tag ) . ",')
1483 WHERE tags LIKE '%," . esc_sql( $old_tag ) . ",%'"
1484 );
1485
1486 $topics = WPF()->db->get_results(
1487 "SELECT `topicid`, `tags`
1488 FROM `" . WPF()->tables->topics . "`
1489 WHERE FIND_IN_SET('" . esc_sql( $old_tag ) . "', tags)",
1490 ARRAY_A
1491 );
1492 if( ! empty( $topics ) ) {
1493 foreach( $topics as $topic ) {
1494 $tags = explode( ',', $topic['tags'] );
1495 if( ! empty( $tags ) ) {
1496 foreach( $tags as $key => $tag ) {
1497 if( $tag === $old_tag ) $tags[ $key ] = $new_tag;
1498 }
1499 $tags = implode( ',', $tags );
1500 WPF()->db->update( WPF()->tables->topics, [ 'tags' => $tags ], [ 'topicid' => $topic['topicid'] ], [ '%s' ], [ '%d' ] );
1501 }
1502 }
1503 }
1504 }
1505 }
1506
1507 public function get_tag( $args = [] ) {
1508 if( $args ) {
1509 $default = [
1510 'tagid' => null,
1511 'tag' => null,
1512 ];
1513 if( is_numeric( $args ) ) {
1514 $args = [ 'tagid' => $args ];
1515 } elseif( is_string( $args ) ) {
1516 $args = [ 'tag' => $args ];
1517 }
1518
1519 $args = wpforo_parse_args( $args, $default );
1520 $sql = "SELECT * FROM `" . WPF()->tables->tags . "`";
1521 $wheres = [];
1522 if( $args['tagid'] ) $wheres[] = "`tagid` = " . intval( $args['tagid'] );
1523 if( $args['tag'] ) $wheres[] = "`tag` = '" . esc_sql( $args['tag'] ) . "'";
1524 if( $wheres ) {
1525 $sql .= " WHERE " . implode( " AND ", $wheres );
1526
1527 if( WPF()->ram_cache->exists( $sql ) ) {
1528 $tag = WPF()->ram_cache->get( $sql );
1529 } else {
1530 $tag = WPF()->db->get_row( $sql, ARRAY_A );
1531 WPF()->ram_cache->set( $sql, $tag );
1532 }
1533
1534 return $tag;
1535 }
1536 }
1537
1538 return [];
1539 }
1540
1541 public function get_tags( $args = [], &$items_count = 0 ) {
1542 $cache = WPF()->cache->on();
1543 $default = [
1544 'tag' => '',
1545 'prefix' => 0,
1546 'count' => null,
1547 'orderby' => 'count',
1548 'order' => 'DESC',
1549 'offset' => null,
1550 'row_count' => null,
1551 ];
1552
1553 $args = wpforo_parse_args( $args, $default );
1554 $sql = "SELECT * FROM `" . WPF()->tables->tags . "`";
1555 $wheres = [];
1556 $wheres[] = " `prefix` = " . intval( $args['prefix'] );
1557 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
1558 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
1559 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
1560
1561 $sql .= " ORDER BY `" . esc_sql( $args['orderby'] ) . "` " . esc_sql( $args['order'] );
1562
1563 if( $args['row_count'] != null ) {
1564 if( $args['offset'] != null ) {
1565 $sql .= " LIMIT " . intval( $args['offset'] ) . ',' . intval( $args['row_count'] );
1566 } else {
1567 $sql .= " LIMIT " . intval( $args['row_count'] );
1568 }
1569 }
1570
1571 if( $cache ) {
1572 $object_key = md5( $sql . WPF()->current_user_groupid );
1573 $object_cache = WPF()->cache->get( $object_key, 'loop', 'tag' );
1574 if( ! empty( $object_cache ) ) {
1575 $items_count = $object_cache['items_count'];
1576
1577 return $object_cache['items'];
1578 }
1579 }
1580
1581 $tags = WPF()->db->get_results( $sql, ARRAY_A );
1582 $tags = apply_filters( 'wpforo_get_tags', $tags );
1583
1584 if( $cache && isset( $object_key ) && ! empty( $tags ) ) {
1585 self::$cache['tags'][ $object_key ]['items'] = $tags;
1586 self::$cache['tags'][ $object_key ]['items_count'] = $items_count;
1587 WPF()->cache->create( 'loop', self::$cache, 'tag' );
1588 }
1589
1590 return $tags;
1591 }
1592
1593 public function sanitize_tags( $tags, $array = false, $limit = false ) {
1594 if( $tags ) {
1595 $lowcase = wpforo_setting( 'tags', 'lowercase' );
1596 if( ! is_array( $tags ) ) {
1597 $tags = wp_unslash( $tags );
1598 $tags = explode( ',', $tags );
1599 }
1600 if( $lowcase ) {
1601 if( function_exists( 'mb_strtolower' ) ) {
1602 $tags = array_map( 'mb_strtolower', $tags );
1603 } else {
1604 $tags = array_map( 'strtolower', $tags );
1605 }
1606 }
1607 $length = wpforo_setting( 'tags', 'length' );
1608 $mb_substr = function_exists( 'mb_substr' );
1609 foreach( $tags as $key => $tag ) {
1610 if( $mb_substr ) {
1611 $tags[ $key ] = mb_substr( $tag, 0, $length );
1612 } else {
1613 $tags[ $key ] = substr( $tag, 0, $length );
1614 }
1615 }
1616 $tags = array_map( 'trim', $tags );
1617 $tags = array_map( 'sanitize_text_field', $tags );
1618 $tags = array_filter( $tags );
1619 $tags = array_unique( $tags );
1620 if( $limit ) {
1621 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1622 }
1623 if( $array ) {
1624 return $tags;
1625 } else {
1626 return implode( ',', $tags );
1627 }
1628 }
1629
1630 if( $array ) {
1631 return [];
1632 } else {
1633 return '';
1634 }
1635 }
1636
1637 public function sanitize_tag( $tag ) {
1638 $tag = trim( $tag );
1639 $tag = wp_unslash( $tag );
1640 $tag = sanitize_text_field( $tag );
1641 $length = wpforo_setting( 'tags', 'length' );
1642 $tag = ( function_exists( 'mb_substr' ) ) ? mb_substr( $tag, 0, $length ) : substr( $tag, 0, $length );
1643 $lowcase = wpforo_setting( 'tags', 'lowercase' );
1644 if( $lowcase ) {
1645 $tag = ( function_exists( 'mb_strtolower' ) ) ? mb_strtolower( $tag ) : strtolower( $tag );
1646 }
1647
1648 return $tag;
1649 }
1650
1651 public function after_add_post( $post, $topic ) {
1652 if( !intval( $post['status'] ) ){
1653 $this->rebuild_first_last( $topic );
1654 $this->rebuild_stats( $topic );
1655 }
1656 }
1657
1658 public function after_delete_post( $post ) {
1659 if( !intval( $post['status'] ) ){
1660 $this->rebuild_first_last( $post['topicid'] );
1661 $this->rebuild_stats( $post['topicid'] );
1662 }
1663 }
1664
1665 public function after_post_status_update( $post ) {
1666 if( $topic = $this->get_topic( $post['topicid'] ) ){
1667 $this->rebuild_first_last( $topic );
1668 $this->rebuild_stats( $topic );
1669 }
1670 }
1671
1672 public function after_delete_user( $userid, $reassign ) {
1673 if( $boardids = WPF()->board->get_active_boardids() ){
1674 if( is_null( $reassign ) ) {
1675
1676 foreach( $boardids as $boardid ){
1677 WPF()->change_board( $boardid );
1678 if( $topicids = WPF()->db->get_col( WPF()->db->prepare( "SELECT `topicid` FROM `" . WPF()->tables->topics . "` WHERE userid = %d", $userid ) ) ) {
1679 foreach( $topicids as $topicid ) $this->delete( $topicid, false, false );
1680 }
1681 }
1682
1683 } else {
1684 if( $reassign = wpforo_bigintval( $reassign ) ){
1685 $data = [ 'userid' => $reassign ];
1686 $format = [ '%d' ];
1687 }else{
1688 $data = [
1689 'userid' => 0,
1690 'name' => wpforo_phrase( 'Anonymous', false ) . " " . $userid,
1691 'email' => "anonymous_$userid@example.com",
1692 ];
1693 $format = [ '%d', '%s', '%s' ];
1694 }
1695
1696 foreach( $boardids as $boardid ){
1697 WPF()->change_board( $boardid );
1698 WPF()->db->update( WPF()->tables->topics, $data, [ 'userid' => $userid ], $format, ['%d'] );
1699 }
1700
1701 }
1702 }
1703 }
1704 }
1705