PluginProbe
wpForo Forum / 2.0.8
wpForo Forum v2.0.8
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.8, at classes/Topics.php

1,693 lines 59.2 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', $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 );
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 if( ! $current ) $current = WPF()->current_object['topic'];
913
914 $sql = "UPDATE `" . WPF()->tables->posts . "` SET `topicid` = %d, `forumid` = %d, `private` = %d,`is_first_post` = 0";
915 $sql = WPF()->db->prepare( $sql, $target['topicid'], $target['forumid'], (int) wpfval( $target, 'private' ) );
916
917 if( $append ) {
918 $sql .= ", `modified` = %s, `created` = %s";
919 $sql = WPF()->db->prepare( $sql, current_time( 'mysql', 1 ), current_time( 'mysql', 1 ) );
920 }
921
922 if( $to_target_title ) {
923 $layout = WPF()->forum->get_layout( $target['forumid'] );
924 $phrase = ( $layout == 3 ? wpforo_phrase( 'Answer to', false ) : wpforo_phrase( 'RE', false ) );
925 $title = $phrase . ': ' . $target['title'];
926 $sql .= ", `title` = %s";
927 $sql = WPF()->db->prepare( $sql, $title );
928 }
929
930 $sql .= " WHERE `topicid` = %d";
931 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
932
933 if( $postids ) {
934 $postids = (array) $postids;
935 $postids = array_map( 'wpforo_bigintval', $postids );
936
937 $sql .= " AND `postid` IN(" . implode( ',', $postids ) . ")";
938 }
939
940 do_action( 'wpforo_before_merge_topic', $target, $current, $postids, $to_target_title, $append );
941
942 $db_resp = WPF()->db->query( $sql );
943
944 if( $db_resp !== false ) {
945 $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
946 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
947 if( ! WPF()->db->get_var( $sql ) ) {
948 $this->delete( $current['topicid'], true, false );
949 } else {
950 $this->rebuild_first_last( $current );
951 $this->rebuild_stats( $current );
952 $this->rebuild_threads( $current );
953 wpforo_clean_cache( 'topic', $current['topicid'], $current );
954 }
955
956 $this->rebuild_first_last( $target );
957 $this->rebuild_stats( $target );
958 $this->rebuild_threads( $target );
959
960 do_action( 'wpforo_after_merge_topic', $target, $current, $postids, $to_target_title, $append );
961
962 WPF()->notice->clear();
963 WPF()->notice->add( 'Done!', 'success' );
964
965 wpforo_clean_cache();
966
967 return true;
968 }
969
970 WPF()->notice->add( 'Data merging error', 'error' );
971 return false;
972 }
973
974 public function split( $args, $to_target_title = 0 ) {
975 if( ! $args ) return false;
976
977 $args['name'] = ( isset( $args['name'] ) ? strip_tags( $args['name'] ) : '' );
978 $args['email'] = ( isset( $args['email'] ) ? sanitize_email( $args['email'] ) : '' );
979
980 if( ! isset( $args['forumid'] ) || ! $args['forumid'] = intval( $args['forumid'] ) ) {
981 WPF()->notice->add( 'Please select a target forum', 'error' );
982
983 return false;
984 }
985
986 if( ! isset( $args['title'] ) || ! $args['title'] = trim( strip_tags( $args['title'] ) ) ) {
987 WPF()->notice->add( 'Please insert required fields', 'error' );
988
989 return false;
990 }
991
992 if( empty( $args['postids'] ) ) {
993 WPF()->notice->add( 'Please select at least one post to split', 'error' );
994
995 return false;
996 }
997
998 $args['postids'] = array_values( $args['postids'] );
999
1000 if( $fpost = WPF()->post->get_post( $args['postids'][0] ) ) {
1001 $args['title'] = wpforo_text( $args['title'], 250, false );
1002 $args['slug'] = ( isset( $args['slug'] ) && $args['slug'] ) ? sanitize_title( $args['slug'] ) : ( ( isset( $args['title'] ) ) ? sanitize_title( $args['title'] ) : md5( time() ) );
1003 if( ! trim( $args['slug'] ) ) $args['slug'] = md5( time() );
1004 $args['slug'] = $this->unique_slug( $args['slug'] );
1005
1006
1007 $args['body'] = $fpost['body'];
1008 $args['created'] = $fpost['created'];
1009 $args['userid'] = $fpost['userid'];
1010 $args['name'] = $fpost['name'];
1011 $args['email'] = $fpost['email'];
1012
1013 extract( $args );
1014
1015 if( isset( $forumid ) ) $forumid = intval( $forumid );
1016 if( isset( $title ) ) $title = sanitize_text_field( trim( $title ) );
1017 if( isset( $slug ) ) $slug = sanitize_title( $slug );
1018 if( isset( $created ) ) $created = sanitize_text_field( $created );
1019 if( isset( $userid ) ) $userid = intval( $userid );
1020 $type = ( isset( $type ) && $type ? 1 : 0 );
1021 $status = ( isset( $status ) && $status ? 1 : 0 );
1022 $private = ( isset( $private ) && $private ? 1 : 0 );
1023 if( isset( $name ) ) $name = strip_tags( trim( $name ) );
1024 if( isset( $email ) ) $email = strip_tags( trim( $email ) );
1025 $has_attach = ( isset( $has_attach ) && $has_attach ) ? 1 : ( ( strpos( $body, '[attach]' ) !== false ) ? 1 : 0 );
1026
1027 if( WPF()->db->insert(
1028 WPF()->tables->topics,
1029 [
1030 'title' => stripslashes(
1031 $title
1032 ),
1033 'slug' => $slug,
1034 'forumid' => $forumid,
1035 'userid' => $userid,
1036 'type' => $type,
1037 'status' => $status,
1038 'private' => $private,
1039 'created' => $created,
1040 'modified' => $created,
1041 'last_post' => 0,
1042 'views' => 0,
1043 'posts' => 1,
1044 'has_attach' => $has_attach,
1045 'name' => $name,
1046 'email' => $email,
1047 ],
1048 [ '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s' ]
1049 ) ) {
1050 $args['topicid'] = $topicid = WPF()->db->insert_id;
1051
1052 if( $this->merge( $args, WPF()->current_object['topic'], $args['postids'], $to_target_title ) ) {
1053 WPF()->notice->clear();
1054 WPF()->notice->add( 'Done!', 'success' );
1055
1056 return $topicid;
1057 }
1058 }
1059 }
1060
1061 WPF()->notice->add( 'Topic splitting error', 'error' );
1062
1063 return false;
1064 }
1065
1066 public function _get_topic_url( $topic, $forum = [] ) {
1067 if( ! is_array( $topic ) ) $topic = $this->get_topic( $topic );
1068 if( $topic ) {
1069 if( is_array( $forum ) && ! empty( $forum ) ) {
1070 $forum_slug = $forum['slug'];
1071 } else {
1072 $forum_slug = '';
1073 if( wpfval( $topic, 'forumid' ) ) {
1074 $forum_slug = $this->get_forumslug( $topic['forumid'] );
1075 } elseif( wpfval( $topic, 'topicid' ) ) {
1076 $forum_slug = $this->get_forumslug_byid( $topic['topicid'] );
1077 }
1078 }
1079 if( wpfval( $topic, 'slug' ) ) return wpforo_home_url( $forum_slug . '/' . $topic['slug'] );
1080 }
1081
1082 return wpforo_home_url();
1083 }
1084
1085 public function get_topic_url( $topic, $forum = [] ) {
1086 return wpforo_ram_get( [ $this, '_get_topic_url' ], $topic, $forum );
1087 }
1088
1089
1090 function get_count( $args = [] ) {
1091 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->topics . "`";
1092 if( ! empty( $args ) ) {
1093 $wheres = [];
1094 foreach( $args as $key => $value ) $wheres[] = "`$key` = '" . esc_sql( $value ) . "'";
1095 if( $wheres ) $sql .= " WHERE " . implode( ' AND ', $wheres );
1096 }
1097
1098 return WPF()->db->get_var( $sql );
1099 }
1100
1101 public function set_status( $topicid, $status ) {
1102 $topicid = wpforo_bigintval( $topicid );
1103 $status = intval( $status );
1104 if( ! $topic = $this->get_topic( $topicid, false ) ) return false;
1105
1106 if( $r = WPF()->db->update( WPF()->tables->topics, [ 'status' => $status ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] ) ) {
1107 if( $status ) {
1108 do_action( 'wpforo_topic_unapprove', $topic );
1109 if( wpfval( $topic, 'tags' ) ) $this->remove_tags( $topic['tags'] );
1110 } else {
1111 do_action( 'wpforo_topic_approve', $topic );
1112 if( wpfval( $topic, 'tags' ) ) $this->add_tags( $topic['tags'] );
1113 }
1114
1115 do_action( 'wpforo_topic_status_update', $topic, $status );
1116 wpforo_clean_cache( 'topic', $topicid );
1117 }
1118
1119 if( $r !== false ){
1120 WPF()->notice->add( 'Done!', 'success' );
1121 return true;
1122 }
1123
1124 WPF()->notice->add( 'Status changing error', 'error' );
1125 return false;
1126 }
1127
1128 public function wprivate( $topicid, $private ) {
1129 WPF()->db->update( WPF()->tables->topics, [ 'private' => $private ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] );
1130
1131 WPF()->db->update( WPF()->tables->posts, [ 'private' => $private ], [ 'topicid' => $topicid ], [ '%d' ], [ '%d' ] );
1132
1133 do_action( 'wpforo_topic_private_update', $topicid, $private );
1134 wpforo_clean_cache();
1135
1136 return true;
1137 }
1138
1139 public function delete_attachments( $topicid ) {
1140 $args = [ 'topicid' => $topicid ];
1141 $posts = WPF()->post->get_posts( $args );
1142 if( ! empty( $posts ) ) {
1143 foreach( $posts as $post ) {
1144 WPF()->post->delete_attachments( $post['postid'] );
1145 }
1146 }
1147 }
1148
1149 public function rebuild_stats( $topic ) {
1150 if( ! $topic ) return false;
1151 if( is_numeric( $topic ) ) $topic = $this->get_topic( $topic );
1152 if( ! is_array( $topic ) || ! $topic ) return false;
1153
1154 $posts = WPF()->post->get_count( [ 'topicid' => $topic['topicid'], 'status' => 0 ] );
1155
1156 $data = [ 'posts' => $posts, 'answers' => 0 ];
1157 $data_format = [ '%d', '%d' ];
1158
1159 $layout = WPF()->forum->get_layout( $topic['forumid'] );
1160 if( $layout === 3 ) {
1161 $data['answers'] = WPF()->post->get_count( [ 'topicid' => $topic['topicid'], 'status' => 0, 'parentid' => 0, 'is_first_post' => 0 ] );
1162 $data['posts'] = $posts - 1;
1163 }
1164
1165 if( $r = WPF()->db->update( WPF()->tables->topics, $data, [ 'topicid' => $topic['topicid'] ], $data_format, [ '%d' ] ) ) {
1166 wpforo_clean_cache( 'topic', $topic['topicid'], $topic );
1167 }
1168
1169 return $r !== false;
1170 }
1171
1172 public function rebuild_first_last( $topic ) {
1173 if( ! $topic ) return false;
1174 if( is_numeric( $topic ) ) $topic = $this->get_topic( $topic );
1175 if( ! is_array( $topic ) || ! $topic ) return false;
1176
1177 $sql = "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d ORDER BY `is_first_post` DESC, `created` ASC, `postid` ASC LIMIT 1";
1178 if( $first_postid = WPF()->db->get_var( WPF()->db->prepare( $sql, $topic['topicid'] ) ) ) {
1179 $sql = "UPDATE `" . WPF()->tables->posts . "` SET `is_first_post` = 1 WHERE `postid` = %d";
1180 WPF()->db->query( WPF()->db->prepare( $sql, $first_postid ) );
1181
1182 do_action( 'wpforo_after_is_first_post_update', $first_postid, 1 );
1183 } else {
1184 $first_postid = 0;
1185 }
1186
1187 $sql = "SELECT `postid`, `created`
1188 FROM `" . WPF()->tables->posts . "`
1189 WHERE `topicid` = %d
1190 ORDER BY `is_first_post` ASC, `created` DESC, `postid` DESC LIMIT 1";
1191 if( ! $last_post = WPF()->db->get_row( WPF()->db->prepare( $sql, $topic['topicid'] ), ARRAY_A ) ) {
1192 $last_post = [ 'postid' => 0, 'created' => $topic['modified'] ];
1193 }
1194
1195 if( $r = WPF()->db->update(
1196 WPF()->tables->topics,
1197 [
1198 'first_postid' => $first_postid,
1199 'last_post' => $last_post['postid'],
1200 'modified' => $last_post['created'],
1201 ],
1202 [ 'topicid' => $topic['topicid'] ],
1203 [ '%d', '%d', '%s' ],
1204 [ '%d' ]
1205 ) ) {
1206 wpforo_clean_cache( 'topic' );
1207 }
1208
1209 return $r !== false;
1210 }
1211
1212 function rebuild_threads( $topicid, $root = null ) {
1213 if( ! is_null( $root ) && $root <= 0 ) return;
1214 if( is_array( $topicid ) && wpfval( $topicid, 'topicid' ) ) $topicid = $topicid['topicid'];
1215 if( $topicid ) {
1216 $posts = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->posts . "` WHERE `topicid` = " . intval( $topicid ), ARRAY_A );
1217 if( ! empty( $posts ) ) {
1218 $threads = [];
1219 foreach( $posts as $post ) {
1220 $threads[ $post['postid'] ] = [ 'postid' => $post['postid'], 'parentid' => $post['parentid'] ];
1221 }
1222 unset( $posts );
1223 foreach( $threads as $item ) {
1224 if( ! is_null( $root ) && $root != $item['postid'] ) continue;
1225 if( ! $item['parentid'] ) {
1226 $thread = WPF()->post->build_thread_data( $item['postid'], $threads );
1227 if( wpfval( $thread, 'children' ) ) {
1228 $children = implode( ',', $thread['children'] );
1229 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = 0 WHERE `postid` = " . intval( $item['postid'] ) );
1230 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = " . intval( $item['postid'] ) . " WHERE `postid` IN(" . esc_sql( $children ) . ")" );
1231 }
1232 } elseif( ! wpfkey( $threads, $item['parentid'] ) ) {
1233 WPF()->db->query( "UPDATE `" . WPF()->tables->posts . "` SET `root` = 0, `parentid` = 0 WHERE `postid` = " . intval( $item['postid'] ) );
1234 }
1235 }
1236 }
1237 }
1238 }
1239
1240 function rebuild_forum_threads( $forumid = 0 ) {
1241 if( ! $forumid ) {
1242 $args = [ 'layout' => 4 ];
1243 $forums = WPF()->forum->get_forums( $args );
1244 if( ! empty( $forums ) ) {
1245 foreach( $forums as $forum ) {
1246 $args = [ 'forumid' => $forum['forumid'] ];
1247 $topics = $this->get_topics( $args );
1248 if( ! empty( $topics ) ) {
1249 foreach( $topics as $topic ) {
1250 $this->rebuild_threads( $topic['topicid'] );
1251 }
1252 }
1253 }
1254 }
1255 } else {
1256 $args = [ 'forumid' => $forumid ];
1257 $topics = $this->get_topics( $args );
1258 if( ! empty( $topics ) ) {
1259 foreach( $topics as $topic ) {
1260 $this->rebuild_threads( $topic['topicid'] );
1261 }
1262 }
1263 }
1264 }
1265
1266 public function members( $topicid, $limit = 0 ) {
1267 if( ! $topicid ) return [];
1268 $members = [];
1269 $args = [
1270 'topicid' => $topicid,
1271 'orderby' => 'created',
1272 'order' => 'ASC',
1273 'private' => 0,
1274 'status' => 0,
1275 'cache_type' => 'args',
1276 ];
1277 $posts = WPF()->post->get_posts( $args );
1278 foreach( $posts as $post ) {
1279 if( wpfval( $post, 'userid' ) ) {
1280 $members[ $post['userid'] ] = wpforo_member( $post['userid'] );
1281 if( $limit && count( $members ) >= $limit ) break;
1282 }
1283 }
1284
1285 return array_filter( $members );
1286 }
1287
1288 public function can_answer( $topicid ) {
1289 if( ! $topicid ) return false;
1290 $topic = wpforo_topic( $topicid );
1291 if( wpfval( $topic, 'topicid' ) ) {
1292 if( wpfval( $topic, 'userid' ) ) {
1293 if( ! WPF()->perm->forum_can( 'aot', $topic['forumid'] ) && WPF()->current_userid == $topic['userid'] ) {
1294 return false;
1295 }
1296 } else {
1297 $guest = WPF()->member->get_guest_cookies();
1298 if( wpfval( $topic, 'email' ) && wpfval( $guest, 'email' ) ) {
1299 if( ! WPF()->perm->forum_can( 'aot', $topic['forumid'] ) && $topic['email'] == $guest['email'] ) {
1300 return false;
1301 }
1302 }
1303 }
1304 }
1305
1306 return true;
1307 }
1308
1309 /**
1310 * @param $topicid
1311 *
1312 * @return bool
1313 */
1314 public function has_is_answer_post( $topicid ) {
1315 if( ! $topicid ) return false;
1316 $sql = "SELECT EXISTS ( SELECT * FROM `" . WPF()->tables->posts . "` WHERE `is_answer` = 1 AND `topicid` = %d) AS is_exists";
1317 $sql = WPF()->db->prepare( $sql, $topicid );
1318
1319 return (bool) WPF()->db->get_var( $sql );
1320 }
1321
1322 public function get_postids( $topicid ) {
1323 $sql = "SELECT `postid`
1324 FROM `" . WPF()->tables->posts . "`
1325 WHERE `topicid` = %d";
1326 $sql = WPF()->db->prepare( $sql, $topicid );
1327 return array_map( 'wpforo_bigintval', WPF()->db->get_col( $sql ) );
1328 }
1329
1330 public function add_tags( $tags ) {
1331 if( $tags ) {
1332 $tags = $this->sanitize_tags( $tags, true );
1333 if( ! empty( $tags ) ) {
1334 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1335 foreach( $tags as $tag ) {
1336 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1337 if( $count ) {
1338 WPF()->db->update( WPF()->tables->tags, [ 'count' => $count + 1 ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1339 } else {
1340 $this->add_tag( $tag, 1 );
1341 }
1342 wpforo_clean_cache( 'tag', $tag );
1343 }
1344 wpforo_clean_cache( 'tag' );
1345 }
1346 }
1347 }
1348
1349 public function add_tag( $tag, $count = 0 ) {
1350 $tagid = 0;
1351 if( $tag ) {
1352 $tag = $this->sanitize_tag( $tag );
1353 if( ! WPF()->db->get_var( "SELECT `tagid` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" ) ) {
1354 $tagid = WPF()->db->insert( WPF()->tables->tags, [ 'tag' => $tag, 'prefix' => 0, 'count' => intval( $count ) ], [ '%s', '%d', '%d' ] );
1355 }
1356 }
1357
1358 return $tagid;
1359 }
1360
1361 public function edit_tags( $tags, $topic = [] ) {
1362 $old_tags = ( wpfval( $topic, 'tags' ) ) ? $this->sanitize_tags( $topic['tags'], true ) : false;
1363 if( $tags ) {
1364 $tags = $this->sanitize_tags( $tags, true );
1365 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1366 if( ! empty( $tags ) ) {
1367 if( wpfval( $topic, 'topicid' ) ) {
1368 if( ! empty( $old_tags ) ) {
1369 foreach( $old_tags as $old_tag ) {
1370 if( ! in_array( $old_tag, $tags ) ) {
1371 $this->remove_tags( $old_tag );
1372 }
1373 }
1374 }
1375 }
1376 if( ! wpfval( $topic, 'status' ) && ! wpfval( $topic, 'private' ) ) {
1377 foreach( $tags as $tag ) {
1378 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1379 if( ! $count ) {
1380 WPF()->db->insert( WPF()->tables->tags, [ 'tag' => $tag, 'prefix' => 0, 'count' => 1 ], [ '%s', '%d', '%d' ] );
1381 } elseif( empty( $old_tags ) || ! in_array( $tag, $old_tags ) ) {
1382 WPF()->db->update( WPF()->tables->tags, [ 'count' => ( $count + 1 ) ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1383 }
1384 wpforo_clean_cache( 'tag', $tag );
1385 }
1386 }
1387 }
1388 } else {
1389 if( ! empty( $old_tags ) && wpfval( $topic, 'topicid' ) ) {
1390 WPF()->db->update( WPF()->tables->topics, [ 'tags' => '' ], [ 'topicid' => $topic['topicid'] ], [ '%s' ], [ '%d' ] );
1391 $this->remove_tags( $old_tags );
1392 }
1393 }
1394 wpforo_clean_cache( 'tag' );
1395 }
1396
1397 public function edit_tag( $tag, $old_tag = '', $disconnect = false ) {
1398
1399 $tag_id = wpfval( $tag, 'tagid' );
1400 $tag_name = wpfval( $tag, 'tag' );
1401 $tag_name = $this->sanitize_tag( $tag_name );
1402
1403 if( false !== WPF()->db->update( WPF()->tables->tags, [ 'tag' => $tag_name ], [ 'tagid' => intval( $tag_id ) ], [ '%s' ], [ '%d' ] ) ) {
1404 if( $old_tag ) {
1405 if( $disconnect ) {
1406 $this->remove_tag_from_topics( $old_tag );
1407 WPF()->db->update( WPF()->tables->tags, [ 'count' => 0 ], [ 'tag' => $tag_name ], [ '%d' ], [ '%s' ] );
1408 } else {
1409 if( $old_tag != $tag_name ) {
1410 $this->update_tag_in_topics( $tag_name, $old_tag );
1411 }
1412 $count = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->topics . "` WHERE FIND_IN_SET('" . esc_sql( $tag_name ) . "', tags)" );
1413 WPF()->db->update( WPF()->tables->tags, [ 'count' => intval( $count ) ], [ 'tag' => $tag_name ], [ '%d' ], [ '%s' ] );
1414 }
1415 }
1416 wpforo_clean_cache( 'tag', $tag_name );
1417
1418 return true;
1419 }
1420
1421 return false;
1422 }
1423
1424 public function remove_tags( $tags ) {
1425 if( $tags ) {
1426 $tags = $this->sanitize_tags( $tags, true );
1427 foreach( $tags as $tag ) {
1428 $count = (int) WPF()->db->get_var( "SELECT `count` FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1429 if( $count === 1 ) {
1430 WPF()->db->query( "DELETE FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'" );
1431 } else {
1432 WPF()->db->update( WPF()->tables->tags, [ 'count' => ( $count - 1 ) ], [ 'tag' => $tag ], [ '%d' ], [ '%s' ] );
1433 }
1434 wpforo_clean_cache( 'tag', $tag );
1435 }
1436 }
1437 wpforo_clean_cache( 'tag' );
1438 }
1439
1440 public function remove_tag( $tag ) {
1441 if( $tag ) {
1442 $this->remove_tag_from_topics( $tag );
1443 $sql_delete_tag = "DELETE FROM `" . WPF()->tables->tags . "` WHERE `tag` = '" . esc_sql( $tag ) . "'";
1444 WPF()->db->query( $sql_delete_tag );
1445 wpforo_clean_cache( 'tag', $tag );
1446 }
1447 }
1448
1449 public function remove_tag_from_topics( $tag ) {
1450 if( $tag ) {
1451 WPF()->db->query(
1452 "UPDATE `" . WPF()->tables->topics . "`
1453 SET tags = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', tags, ','), '," . esc_sql( $tag ) . ",', ','))
1454 WHERE FIND_IN_SET('" . esc_sql( $tag ) . "', tags)"
1455 );
1456 }
1457 }
1458
1459 public function update_tag_in_topics( $new_tag, $old_tag ) {
1460 if( $new_tag && $old_tag ) {
1461
1462 WPF()->db->query(
1463 "UPDATE `" . WPF()->tables->topics . "`
1464 SET tags = '" . esc_sql( $new_tag ) . "'
1465 WHERE tags LIKE '" . esc_sql( $old_tag ) . "'"
1466 );
1467
1468 WPF()->db->query(
1469 "UPDATE `" . WPF()->tables->topics . "`
1470 SET tags = REPLACE(tags, '," . esc_sql( $old_tag ) . ",', '," . esc_sql( $new_tag ) . ",')
1471 WHERE tags LIKE '%," . esc_sql( $old_tag ) . ",%'"
1472 );
1473
1474 $topics = WPF()->db->get_results(
1475 "SELECT `topicid`, `tags`
1476 FROM `" . WPF()->tables->topics . "`
1477 WHERE FIND_IN_SET('" . esc_sql( $old_tag ) . "', tags)",
1478 ARRAY_A
1479 );
1480 if( ! empty( $topics ) ) {
1481 foreach( $topics as $topic ) {
1482 $tags = explode( ',', $topic['tags'] );
1483 if( ! empty( $tags ) ) {
1484 foreach( $tags as $key => $tag ) {
1485 if( $tag === $old_tag ) $tags[ $key ] = $new_tag;
1486 }
1487 $tags = implode( ',', $tags );
1488 WPF()->db->update( WPF()->tables->topics, [ 'tags' => $tags ], [ 'topicid' => $topic['topicid'] ], [ '%s' ], [ '%d' ] );
1489 }
1490 }
1491 }
1492 }
1493 }
1494
1495 public function get_tag( $args = [] ) {
1496 if( $args ) {
1497 $default = [
1498 'tagid' => null,
1499 'tag' => null,
1500 ];
1501 if( is_numeric( $args ) ) {
1502 $args = [ 'tagid' => $args ];
1503 } elseif( is_string( $args ) ) {
1504 $args = [ 'tag' => $args ];
1505 }
1506
1507 $args = wpforo_parse_args( $args, $default );
1508 $sql = "SELECT * FROM `" . WPF()->tables->tags . "`";
1509 $wheres = [];
1510 if( $args['tagid'] ) $wheres[] = "`tagid` = " . intval( $args['tagid'] );
1511 if( $args['tag'] ) $wheres[] = "`tag` = '" . esc_sql( $args['tag'] ) . "'";
1512 if( $wheres ) {
1513 $sql .= " WHERE " . implode( " AND ", $wheres );
1514
1515 if( WPF()->ram_cache->exists( $sql ) ) {
1516 $tag = WPF()->ram_cache->get( $sql );
1517 } else {
1518 $tag = WPF()->db->get_row( $sql, ARRAY_A );
1519 WPF()->ram_cache->set( $sql, $tag );
1520 }
1521
1522 return $tag;
1523 }
1524 }
1525
1526 return [];
1527 }
1528
1529 public function get_tags( $args = [], &$items_count = 0 ) {
1530 $cache = WPF()->cache->on();
1531 $default = [
1532 'tag' => '',
1533 'prefix' => 0,
1534 'count' => null,
1535 'orderby' => 'count',
1536 'order' => 'DESC',
1537 'offset' => null,
1538 'row_count' => null,
1539 ];
1540
1541 $args = wpforo_parse_args( $args, $default );
1542 $sql = "SELECT * FROM `" . WPF()->tables->tags . "`";
1543 $wheres = [];
1544 $wheres[] = " `prefix` = " . intval( $args['prefix'] );
1545 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
1546 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
1547 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
1548
1549 $sql .= " ORDER BY `" . esc_sql( $args['orderby'] ) . "` " . esc_sql( $args['order'] );
1550
1551 if( $args['row_count'] != null ) {
1552 if( $args['offset'] != null ) {
1553 $sql .= " LIMIT " . intval( $args['offset'] ) . ',' . intval( $args['row_count'] );
1554 } else {
1555 $sql .= " LIMIT " . intval( $args['row_count'] );
1556 }
1557 }
1558
1559 if( $cache ) {
1560 $object_key = md5( $sql . WPF()->current_user_groupid );
1561 $object_cache = WPF()->cache->get( $object_key, 'loop', 'tag' );
1562 if( ! empty( $object_cache ) ) {
1563 $items_count = $object_cache['items_count'];
1564
1565 return $object_cache['items'];
1566 }
1567 }
1568
1569 $tags = WPF()->db->get_results( $sql, ARRAY_A );
1570 $tags = apply_filters( 'wpforo_get_tags', $tags );
1571
1572 if( $cache && isset( $object_key ) && ! empty( $tags ) ) {
1573 self::$cache['tags'][ $object_key ]['items'] = $tags;
1574 self::$cache['tags'][ $object_key ]['items_count'] = $items_count;
1575 WPF()->cache->create( 'loop', self::$cache, 'tag' );
1576 }
1577
1578 return $tags;
1579 }
1580
1581 public function sanitize_tags( $tags, $array = false, $limit = false ) {
1582 if( $tags ) {
1583 $lowcase = wpforo_setting( 'tags', 'lowercase' );
1584 if( ! is_array( $tags ) ) {
1585 $tags = wp_unslash( $tags );
1586 $tags = explode( ',', $tags );
1587 }
1588 if( $lowcase ) {
1589 if( function_exists( 'mb_strtolower' ) ) {
1590 $tags = array_map( 'mb_strtolower', $tags );
1591 } else {
1592 $tags = array_map( 'strtolower', $tags );
1593 }
1594 }
1595 $length = wpforo_setting( 'tags', 'length' );
1596 $mb_substr = function_exists( 'mb_substr' );
1597 foreach( $tags as $key => $tag ) {
1598 if( $mb_substr ) {
1599 $tags[ $key ] = mb_substr( $tag, 0, $length );
1600 } else {
1601 $tags[ $key ] = substr( $tag, 0, $length );
1602 }
1603 }
1604 $tags = array_map( 'trim', $tags );
1605 $tags = array_map( 'sanitize_text_field', $tags );
1606 $tags = array_filter( $tags );
1607 $tags = array_unique( $tags );
1608 if( $limit ) {
1609 $tags = array_slice( $tags, 0, wpforo_setting( 'tags', 'max_per_topic' ) );
1610 }
1611 if( $array ) {
1612 return $tags;
1613 } else {
1614 return implode( ',', $tags );
1615 }
1616 }
1617
1618 if( $array ) {
1619 return [];
1620 } else {
1621 return '';
1622 }
1623 }
1624
1625 public function sanitize_tag( $tag ) {
1626 $tag = trim( $tag );
1627 $tag = wp_unslash( $tag );
1628 $tag = sanitize_text_field( $tag );
1629 $length = wpforo_setting( 'tags', 'length' );
1630 $tag = ( function_exists( 'mb_substr' ) ) ? mb_substr( $tag, 0, $length ) : substr( $tag, 0, $length );
1631 $lowcase = wpforo_setting( 'tags', 'lowercase' );
1632 if( $lowcase ) {
1633 $tag = ( function_exists( 'mb_strtolower' ) ) ? mb_strtolower( $tag ) : strtolower( $tag );
1634 }
1635
1636 return $tag;
1637 }
1638
1639 public function after_add_post( $post, $topic ) {
1640 if( !intval( $post['status'] ) ){
1641 $this->rebuild_first_last( $topic );
1642 $this->rebuild_stats( $topic );
1643 }
1644 }
1645
1646 public function after_delete_post( $post ) {
1647 if( !intval( $post['status'] ) ){
1648 $this->rebuild_first_last( $post['topicid'] );
1649 $this->rebuild_stats( $post['topicid'] );
1650 }
1651 }
1652
1653 public function after_post_status_update( $post ) {
1654 if( $topic = $this->get_topic( $post['topicid'] ) ){
1655 $this->rebuild_first_last( $topic );
1656 $this->rebuild_stats( $topic );
1657 }
1658 }
1659
1660 public function after_delete_user( $userid, $reassign ) {
1661 if( $boardids = WPF()->board->get_active_boardids() ){
1662 if( is_null( $reassign ) ) {
1663
1664 foreach( $boardids as $boardid ){
1665 WPF()->change_board( $boardid );
1666 if( $topicids = WPF()->db->get_col( WPF()->db->prepare( "SELECT `topicid` FROM `" . WPF()->tables->topics . "` WHERE userid = %d", $userid ) ) ) {
1667 foreach( $topicids as $topicid ) $this->delete( $topicid, false, false );
1668 }
1669 }
1670
1671 } else {
1672 if( $reassign = wpforo_bigintval( $reassign ) ){
1673 $data = [ 'userid' => $reassign ];
1674 $format = [ '%d' ];
1675 }else{
1676 $data = [
1677 'userid' => 0,
1678 'name' => wpforo_phrase( 'Anonymous', false ) . " " . $userid,
1679 'email' => "anonymous_$userid@example.com",
1680 ];
1681 $format = [ '%d', '%s', '%s' ];
1682 }
1683
1684 foreach( $boardids as $boardid ){
1685 WPF()->change_board( $boardid );
1686 WPF()->db->update( WPF()->tables->topics, $data, [ 'userid' => $userid ], $format, ['%d'] );
1687 }
1688
1689 }
1690 }
1691 }
1692 }
1693