PluginProbe
wpForo Forum / 2.4.2
wpForo Forum v2.4.2
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.4.2, at classes/Topics.php

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