PluginProbe
wpForo Forum / 2.2.9
wpForo Forum v2.2.9
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / classes / Topics.php

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

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