PluginProbe
wpForo Forum / 2.3.0
wpForo Forum v2.3.0
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 / Forums.php

Forums.php in wpForo Forum 2.3.0, at classes/Forums.php

1,360 lines 53.7 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 use stdClass;
6
7 // Exit if accessed directly
8 if( ! defined( 'ABSPATH' ) ) exit;
9
10 class Forums {
11 static $cache = [ 'forums' => [], 'forum' => [], 'item' => [] ];
12
13 public static function _get_forumids( $table, $layout = null ) {
14 $sql = "SELECT `forumid` FROM `$table` WHERE `status` = 1";
15 if( !is_null( $layout ) ) $sql .= WPF()->db->prepare( " AND `layout` = %d", $layout );
16 return WPF()->db->get_col($sql);
17 }
18
19 public static function get_forumids( $table, $layout = null ) {
20 return wpforo_ram_get( [self::class, '_get_forumids'], $table, $layout );
21 }
22
23 public function __construct() {
24 $this->init_hooks();
25 }
26
27 public function reset() {
28 self::$cache = [ 'forums' => [], 'forum' => [], 'item' => [] ];
29 }
30
31 private function init_hooks() {
32 add_action( 'wpforo_after_add_usergroup', [$this, 'after_add_usergroup'] );
33
34 add_action( 'wpforo_after_merge_forum', [$this, 'after_merge_forum'], 10, 2 );
35
36 add_action( 'wpforo_after_add_topic', [$this, 'after_add_topic'] );
37 add_action( 'wpforo_after_delete_topic', [$this, 'after_delete_topic'] );
38 add_action( 'wpforo_topic_status_update', [$this, 'after_topic_status_update'] );
39 add_action( 'wpforo_topic_private_update', [$this, 'topic_private_update'] );
40 add_action( 'wpforo_after_merge_topic', [$this, 'after_merge_topic'], 10, 2 );
41 add_action( 'wpforo_after_move_topic', [$this, 'after_move_topic'], 10, 2 );
42
43 add_action( 'wpforo_after_add_post', [$this, 'after_add_post'], 10, 3 );
44 add_action( 'wpforo_after_delete_post', [$this, 'after_delete_post'] );
45 add_action( 'wpforo_post_status_update', [$this, 'after_post_status_update'] );
46 }
47
48 public function get_cache( $var ) {
49 if( isset( self::$cache[ $var ] ) ) return self::$cache[ $var ];
50 }
51
52 private function unique_slug( $slug, $parentid = 0, $forumid = 0 ) {
53 $new_slug = wpforo_text( $slug, 250, false );
54 $forumid = intval( $forumid );
55 $i = 2;
56 while( ! WPF()->can_use_this_slug( $new_slug ) || WPF()->db->get_var( "SELECT `forumid` FROM " . WPF()->tables->forums . " WHERE `slug` = '" . esc_sql( $new_slug ) . "'" . ( $forumid ? ' AND `forumid` != ' . intval( $forumid ) : '' ) ) ) {
57 if( ! isset( $parent_slug ) && $parentid = intval( $parentid ) ) {
58 $parent_slug = WPF()->db->get_var( "SELECT `slug` FROM " . WPF()->tables->forums . " WHERE `forumid` = " . intval( $parentid ) );
59 $new_slug = $parent_slug . "-" . wpforo_text( $slug, 250, false );
60 } else {
61 $new_slug = wpforo_text( $slug, 250, false ) . '-' . $i ++;
62 }
63 }
64
65 return $new_slug;
66 }
67
68 public function copy( $forumid ) {
69 if( ( $forumid = intval( $forumid ) )
70 && ( $forum = $this->get_forum( $forumid ) )
71 ){
72 $forum['title'] .= ' - COPY';
73 $forum['last_topicid'] = $forum['last_postid'] = $forum['last_userid'] = $forum['topics'] = $forum['posts'] = 0;
74 $forum['last_post_date'] = '0000-00-00 00:00:00';
75 $forum['permission'] = unserialize( $forum['permissions'] );
76 unset( $forum['forumid'], $forum['url'], $forum['cover_url'], $forum['permissions'] );
77
78 $forum['forumid'] = $this->add( $forum, false );
79
80 do_action( 'wpforo_after_copy_forum', $forum, $forumid );
81 return $forum['forumid'];
82 }
83
84 WPF()->notice->add( 'Can\'t copy forum', 'error' );
85
86 return false;
87 }
88
89 public function add( $args = [], $checkperm = true ) {
90 if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
91 WPF()->notice->add( 'Permission denied for add forum', 'error' );
92
93 return false;
94 }
95
96 if( empty( $args ) && empty( $_REQUEST['forum'] ) ) return false;
97 if( empty( $args ) && ! empty( $_REQUEST['forum'] ) ) $args = $_REQUEST['forum'];
98
99 $args['title'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'title' ) ) );
100 if( ! $args['title'] ) {
101 WPF()->notice->add( 'Please insert required fields!', 'error' );
102
103 return false;
104 }
105
106 $args['title'] = wpforo_text( $args['title'], 250, false );
107 $args['parentid'] = (int) wpfval( $args, 'parentid' );
108
109 $args['slug'] = ( $ss = trim( sanitize_title( (string) wpfval( $args, 'slug' ) ) ) ) ? $ss : ( ( $st = trim( sanitize_title( $args['title'] ) ) ) ? $st : md5( time() ) );
110 $args['slug'] = $this->unique_slug( $args['slug'], $args['parentid'] );
111
112 $args['description'] = wpforo_kses( stripslashes( (string) wpfval( $args, 'description' ) ) );
113
114 $group_access_relation = WPF()->usergroup->get_usergroup_access_relation();
115 $args['permission'] = ( (array) wpfval( $args, 'permission' ) ) + $group_access_relation;
116 $args['permission'] = serialize( array_map( 'sanitize_text_field', $args['permission'] ) );
117
118 $args['meta_key'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'meta_key' ) ) );
119 $args['meta_desc'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'meta_desc' ) ) );
120 $args['icon'] = sanitize_text_field( (string) wpfval( $args, 'icon' ) );
121 $args['cover'] = wpforo_bigintval( wpfval( $args, 'cover' ) );
122 $args['cover_height'] = intval( wpfval( $args, 'cover_height' ) );
123 if( ! $args['cover_height'] ) $args['cover_height'] = 150;
124 $args['topics'] = (int) wpfval( $args, 'topics' );
125 $args['posts'] = (int) wpfval( $args, 'posts' );
126 $args['order'] = (int) wpfval( $args, 'order' );
127
128 $args['status'] = (int) wpfval( $args, 'status' );
129 if( ! $args['status'] ) $args['status'] = 1;
130
131 $args['color'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'color' ) ) );
132 if( ! $args['color'] ) $args['color'] = '#888888';
133
134 $args['is_cat'] = (int) wpfval( $args, 'is_cat' );
135 if( ! $args['parentid'] ) $args['is_cat'] = 1;
136
137 $args['layout'] = (int) wpfval( $args, 'layout' );
138 if( $args['parentid'] ) {
139 $layout = (int) WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . $args['parentid'] );
140 if( $layout ) $args['layout'] = $layout;
141 }
142 if( ! $args['layout'] ) $args['layout'] = 1;
143
144 $args = apply_filters( 'wpforo_before_add_forum', $args, $checkperm );
145 if( ! $args ) return false;
146
147 if( WPF()->db->insert( WPF()->tables->forums,
148 [
149 'title' => $args['title'],
150 'slug' => $args['slug'],
151 'description' => $args['description'],
152 'parentid' => $args['parentid'],
153 'icon' => $args['icon'],
154 'cover' => $args['cover'],
155 'cover_height' => $args['cover_height'],
156 'topics' => $args['topics'],
157 'posts' => $args['posts'],
158 'permissions' => $args['permission'],
159 'meta_key' => $args['meta_key'],
160 'meta_desc' => $args['meta_desc'],
161 'status' => $args['status'],
162 'is_cat' => $args['is_cat'],
163 'layout' => $args['layout'],
164 'order' => $args['order'],
165 'color' => $args['color'],
166 ],
167 [ '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%d', '%s', ]
168 ) ) {
169 $args['forumid'] = WPF()->db->insert_id;
170
171 do_action( 'wpforo_after_add_forum', $args, $checkperm );
172
173 $this->delete_tree_cache();
174 wpforo_clean_cache();
175 WPF()->notice->add( 'Your forum successfully added', 'success' );
176
177 return $args['forumid'];
178 }
179
180 WPF()->notice->add( 'Can\'t add forum', 'error' );
181
182 return false;
183 }
184
185 public function edit( $args = [], $checkperm = true ) {
186 if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
187 WPF()->notice->add( 'Permission denied for edit forum', 'error' );
188
189 return false;
190 }
191
192 if( empty( $args ) && empty( $_REQUEST['forum'] ) ) return false;
193 if( empty( $args ) && ! empty( $_REQUEST['forum'] ) ) $args = $_REQUEST['forum'];
194 if( ! isset( $args['forumid'] ) && isset( $_GET['id'] ) ) $args['forumid'] = $_GET['id'];
195
196 $args['forumid'] = (int) wpfval( $args, 'forumid' );
197 if( ! $forum = $this->get_forum( $args['forumid'] ) ) {
198 WPF()->notice->add( 'Forum update error', 'error' );
199
200 return false;
201 }
202
203 $args['title'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'title' ) ) );
204 if( ! $args['title'] ) {
205 WPF()->notice->add( 'Please insert required fields!', 'error' );
206
207 return false;
208 }
209
210 $args['title'] = wpforo_text( $args['title'], 250, false );
211 $args['parentid'] = (int) wpfval( $args, 'parentid' );
212 if( $args['forumid'] === $args['parentid'] ) $args['parentid'] = (int) $forum['parentid'];
213
214 $args['slug'] = ( $ss = trim( sanitize_title( (string) wpfval( $args, 'slug' ) ) ) ) ? $ss : ( ( $st = trim( sanitize_title( $args['title'] ) ) ) ? $st : md5( time() ) );
215 $args['slug'] = $this->unique_slug( $args['slug'], $args['parentid'], $args['forumid'] );
216
217 $args['description'] = wpforo_kses( stripslashes( (string) wpfval( $args, 'description' ) ) );
218
219 $group_access_relation = WPF()->usergroup->get_usergroup_access_relation();
220 $args['permission'] = ( (array) wpfval( $args, 'permission' ) ) + $group_access_relation;
221 $args['permission'] = serialize( array_map( 'sanitize_text_field', $args['permission'] ) );
222
223 $args['meta_key'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'meta_key' ) ) );
224 $args['meta_desc'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'meta_desc' ) ) );
225 $args['icon'] = sanitize_text_field( wpfval( $args, 'icon' ) );
226 $args['cover'] = wpforo_bigintval( wpfval( $args, 'cover' ) );
227 $args['cover_height'] = intval( wpfval( $args, 'cover_height' ) );
228 if( ! $args['cover_height'] ) $args['cover_height'] = 150;
229 $args['topics'] = (int) wpfval( $args, 'topics' );
230 $args['posts'] = (int) wpfval( $args, 'posts' );
231 $args['order'] = (int) wpfval( $args, 'order' );
232
233 $args['status'] = (int) wpfval( $args, 'status' );
234 if( ! $args['status'] ) $args['status'] = 1;
235
236 $args['color'] = sanitize_text_field( stripslashes( (string) wpfval( $args, 'color' ) ) );
237 if( ! $args['color'] ) $args['color'] = '#888888';
238
239 $args['is_cat'] = (int) wpfval( $args, 'is_cat' );
240 if( ! $args['parentid'] ) $args['is_cat'] = 1;
241
242 $args['layout'] = (int) wpfval( $args, 'layout' );
243 if( $args['parentid'] ) {
244 $layout = (int) WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . $args['parentid'] );
245 if( $layout ) $args['layout'] = $layout;
246 }
247 if( ! $args['layout'] ) $args['layout'] = 1;
248
249 $args = apply_filters( 'wpforo_before_edit_forum', $args, $forum, $checkperm );
250 if( ! $args ) return false;
251
252 if( false !== WPF()->db->update( WPF()->tables->forums,
253 [
254 'title' => $args['title'],
255 'slug' => $args['slug'],
256 'description' => $args['description'],
257 'parentid' => $args['parentid'],
258 'icon' => $args['icon'],
259 'cover' => $args['cover'],
260 'cover_height' => $args['cover_height'],
261 'permissions' => $args['permission'],
262 'meta_key' => $args['meta_key'],
263 'meta_desc' => $args['meta_desc'],
264 'status' => $args['status'],
265 'is_cat' => $args['is_cat'],
266 'layout' => $args['layout'],
267 'color' => $args['color'],
268 ],
269 [ 'forumid' => $args['forumid'] ],
270 [ '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s' ],
271 [ '%d' ] ) ) {
272 if( $childs = $this->get_childs( $args['forumid'] ) ) {
273 $sql = "UPDATE `" . WPF()->tables->forums . "` SET `layout` = " . $args['layout'] . " WHERE `forumid` IN(" . implode( ',', $childs ) . ")";
274 WPF()->db->query( $sql );
275 }
276
277 do_action( 'wpforo_after_edit_forum', $args, $forum, $checkperm );
278
279 $this->delete_tree_cache();
280 wpforo_clean_cache();
281 WPF()->notice->add( 'Forum successfully updated', 'success' );
282
283 return $args['forumid'];
284 }
285
286 WPF()->notice->add( 'Forum update error', 'error' );
287
288 return false;
289 }
290
291 public function delete( $forumid = 0, $checkperm = true ) {
292 $forumid = intval( $forumid );
293 if( ! $forumid && isset( $_REQUEST['id'] ) ) $forumid = intval( $_REQUEST['id'] );
294
295 if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
296 WPF()->notice->add( 'Permission denied for delete forum', 'error' );
297
298 return false;
299 }
300
301 $forumids = $this->get_childs( $forumid );
302 $forumids[] = $forumid;
303 foreach( $forumids as $forumid ) $this->__delete( $forumid );
304
305 $this->delete_tree_cache();
306 wpforo_clean_cache();
307 WPF()->notice->add( 'Your forum successfully deleted', 'success' );
308
309 return true;
310 }
311
312 private function __delete( $forumid ) {
313 $forumid = intval( $forumid );
314 // START delete topic posts include first post
315 if( $topicids = WPF()->db->get_col( "SELECT `topicid` FROM " . WPF()->tables->topics . " WHERE `forumid` = " . $forumid ) ) {
316 foreach( $topicids as $topicid ) {
317 WPF()->topic->delete( $topicid, false, false );
318 }
319 }
320 // END delete topic posts include first post
321
322 WPF()->db->delete( WPF()->tables->forums, [ 'forumid' => $forumid ], [ '%d' ] );
323
324 do_action( 'wpforo_after_delete_forum', $forumid );
325 }
326
327 public function merge( $forumid = 0, $mergeid = 0 ) {
328 $forumid = intval( $forumid );
329 $mergeid = intval( $mergeid );
330
331 if( ! $forumid && isset( $_REQUEST['id'] ) ) $forumid = intval( $_REQUEST['id'] );
332 if( ! $mergeid && isset( $_REQUEST['forum']['mergeid'] ) ) $mergeid = intval( $_REQUEST['forum']['mergeid'] );
333
334 if( ! $forumid || ! $mergeid ) return false;
335
336 if( $child_forumids = $this->get_child_forums( $forumid ) ) {
337 $forumids = implode( ',', $child_forumids );
338 $merge_layout = $this->get_layout( $mergeid );
339
340 if( ! WPF()->db->query( "UPDATE " . WPF()->tables->forums . " SET `parentid` = " . $mergeid . ", `layout` = " . $merge_layout . " WHERE `forumid` IN(" . $forumids . ")" ) ) {
341 WPF()->notice->add( 'Forum merging error', 'error' );
342
343 return false;
344 }
345
346 if( $childs = $this->get_childs( $forumid ) ){
347 WPF()->db->query( "UPDATE " . WPF()->tables->forums . " SET `layout` = " . $merge_layout . " WHERE `forumid` IN(" . implode( ',', $childs ) . ")" );
348 }
349 }
350
351 WPF()->db->update( WPF()->tables->topics, [ 'forumid' => $mergeid ], [ 'forumid' => $forumid ], [ '%d' ], [ '%d' ] );
352 WPF()->db->update( WPF()->tables->posts, [ 'forumid' => $mergeid ], [ 'forumid' => $forumid ], [ '%d' ], [ '%d' ] );
353
354 if( WPF()->db->delete( WPF()->tables->forums, [ 'forumid' => $forumid ], [ '%d' ] ) ) {
355
356 do_action( 'wpforo_after_merge_forum', $forumid, $mergeid );
357
358 $this->delete_tree_cache();
359 wpforo_clean_cache( 'forum' );
360 WPF()->notice->add( 'Forum is successfully merged', 'success' );
361 return true;
362 }
363
364 WPF()->notice->add( 'Forum merging error', 'error' );
365
366 return false;
367 }
368
369 public function rebuild_last_infos( $forumid, $update_parent_forums = true ) {
370 if( ! $forumid = intval( $forumid ) ) return;
371
372 $last_topicid = 0;
373 $last_postid = 0;
374 $last_userids = [];
375 $last_post_date = '0000-00-00 00:00:00';
376 $last_users_number = apply_filters( 'wpforo_item_participant_avatars_count', 3 );
377
378 if( $last_topics = WPF()->topic->get_topics( [
379 'forumid' => $forumid,
380 'status' => 0,
381 'private' => 0,
382 'orderby' => 'topicid',
383 'order' => 'DESC',
384 'row_count' => 1,
385 ] ) ) {
386 $last_topic = $last_topics[0];
387 $last_topicid = $last_topic['topicid'];
388 }
389
390 // Collect participant userids from current forum and child forums
391 // Don't use simple GROUP BY, it requires aggregation using INNER JOIN
392 // otherwise it returns unexpected result with missing userids
393 $forumids = WPF()->forum->get_forumid_and_childids( $forumid );
394 $sql = "SELECT p.userid FROM `" . WPF()->tables->posts . "` p
395 INNER JOIN (
396 SELECT `userid`, MAX(`created`) AS LatestCreated
397 FROM `" . WPF()->tables->posts . "`
398 WHERE `status` = 0 AND `private` = 0 AND `forumid` IN (" . esc_sql( implode( ',', $forumids ) ) . ")
399 GROUP BY `userid`
400 ) AS latest_posts ON p.userid = latest_posts.userid AND p.created = latest_posts.LatestCreated
401 WHERE p.`status` = 0 AND p.`private` = 0 AND p.`forumid` IN (" . esc_sql( implode( ',', $forumids ) ) . ")
402 ORDER BY p.`created` DESC, p.`postid` DESC LIMIT " . intval($last_users_number);
403
404 if( $last_posts = WPF()->db->get_results( $sql, ARRAY_A ) ) {
405 foreach ( $last_posts as $last_post ){
406 $last_userids[] = $last_post['userid'];
407 }
408 }
409
410 $sql = "SELECT `postid`, `created` FROM `" . WPF()->tables->posts . "` WHERE `status` = 0 AND `private` = 0 AND `forumid` IN(" . esc_sql( implode( ',', $forumids ) ) . ") ORDER BY `created` DESC, `postid` DESC LIMIT 1";
411 if( $last_post = WPF()->db->get_row( $sql, ARRAY_A ) ) {
412 $last_postid = $last_post['postid'];
413 $last_post_date = $last_post['created'];
414 }
415
416 //Update last information of current forum only (use its own and child participants)
417 $sql = "UPDATE `" . WPF()->tables->forums . "`
418 SET `last_topicid` = %d,
419 `last_postid` = %d,
420 `last_userid` = %s,
421 `last_post_date` = %s
422 WHERE `forumid` = " . intval( $forumid );
423 WPF()->db->query( WPF()->db->prepare( $sql, $last_topicid, $last_postid, implode(',', $last_userids), $last_post_date ) );
424 wpforo_clean_cache( 'forum-soft', $forumid);
425
426 if( $update_parent_forums ){
427 // Update parent forums last info if topics and posts are changed.
428 // Parent forums must not be updated on [reset_forums_stats] action and on recursive calls.
429 $parent_ids = [];
430 $this->get_parents( $forumid, $parent_ids );
431 $parent_ids = array_unique( array_filter( array_map( 'wpforo_bigintval', $parent_ids ) ) );
432 if( !empty( $parent_ids ) ) {
433 // Remove current forumid to only update the parent forums
434 // and prevent updating last userids with parent forum participants
435 if ( ( $current_forumid_key = array_search( $forumid, $parent_ids ) ) !== false ) {
436 unset( $parent_ids[ $current_forumid_key ] );
437 }
438 if( !empty( $parent_ids ) ){
439 foreach ( $parent_ids as $parent_id ){
440 $this->rebuild_last_infos($parent_id, false);
441 wpforo_clean_cache( 'forum-soft', $parent_id );
442 }
443 wpforo_clean_cache( 'forum-soft' );
444 }
445 }
446 }
447 }
448
449 public function rebuild_stats( $forumid ) {
450 if( ! $forumid = intval( $forumid ) ) return false;
451 $topics = WPF()->topic->get_count( [ 'forumid' => $forumid, 'status' => 0, 'private' => 0 ] );
452 $posts = WPF()->post->get_count( [ 'forumid' => $forumid, 'status' => 0, 'private' => 0 ] );
453
454 if( false !== WPF()->db->update( WPF()->tables->forums, [ 'topics' => $topics, 'posts' => $posts ], [ 'forumid' => $forumid ], [ '%d', '%d' ], [ '%d' ] ) ) {
455 $parent_ids = $this->get_parent_forumids_static($forumid);
456 if( !empty( $parent_ids ) ){
457 foreach( $parent_ids as $parent_id ){
458 wpforo_clean_cache( 'forum-soft', $parent_id );
459 }
460 }
461 wpforo_clean_cache( 'forum-soft' );
462 return true;
463 }
464
465 return false;
466 }
467
468 function get_parent_forumids_static( $forumid ){
469 $parent_ids = [];
470 $level_0 = wpforo_forum( $forumid );
471 if( wpfval( $level_0, 'parentid') ) {
472 $level_1 = wpforo_forum( $level_0['parentid'] );
473 $parent_ids[] = $level_1['forumid'];
474 if( wpfval( $level_1, 'parentid') ){
475 $level_2 = wpforo_forum( $level_1['parentid'] );
476 $parent_ids[] = $level_2['forumid'];
477 if( wpfval( $level_2, 'parentid') ){
478 $level_3 = wpforo_forum( $level_2['parentid'] );
479 $parent_ids[] = $level_3['forumid'];
480 }
481 }
482 }
483 return $parent_ids;
484 }
485
486 function _get_forum( $args ) {
487 $forum = [];
488 if( ! $args ) return $forum;
489
490 $default = [
491 'forumid' => null,
492 'slug' => '',
493 'status' => null,
494 'type' => 'all',
495 ];
496 if( ! is_array( $args ) ) {
497 if( is_numeric( $args ) ) {
498 $default['forumid'] = intval( $args );
499 if( $default['forumid'] === WPF()->current_object['forumid'] ) return WPF()->current_object['forum'];
500 } elseif( is_string( $args ) ) {
501 $default['slug'] = trim( (string) $args );
502 if( $default['slug'] && $default['slug'] === wpfval( WPF()->current_object['forum'], 'slug' ) ) return WPF()->current_object['forum'];
503 }
504 }
505 $args = wpforo_parse_args( $args, $default );
506
507 $wheres = [];
508 if( $args['forumid'] ) $wheres[] = "`forumid` = " . intval( $args['forumid'] );
509 if( $args['slug'] ) $wheres[] = "`slug` = '" . esc_sql( $args['slug'] ) . "'";
510 if( ! is_null( $args['status'] ) ) $wheres[] = "`status` = " . intval( $args['status'] );
511 switch( $args['type'] ) {
512 case 'category':
513 $wheres[] = "`is_cat` = 1";
514 break;
515 case 'forum':
516 $wheres[] = "`is_cat` = 0";
517 break;
518 }
519
520 if( $wheres ) {
521 $sql = "SELECT * FROM `" . WPF()->tables->forums . "` WHERE " . implode( " AND ", $wheres );
522 if( $forum = WPF()->db->get_row( $sql, ARRAY_A ) ) {
523 if( ! $forum['layout'] ) $forum['layout'] = 1;
524 $forum['url'] = $this->get_forum_url( $forum );
525 if( $forum['cover'] = wpforo_bigintval( wpfval($forum, 'cover') ) ){
526 $image = wp_get_attachment_image_src( $forum['cover'], 'full' );
527 $forum['cover_url'] = (string) wpfval($image, 0);
528 }else{
529 $forum['cover_url'] = '';
530 }
531 }
532 }
533
534 return apply_filters( 'wpforo_get_forum', $forum, $args );
535 }
536
537 public function get_forum( $args ) {
538 return wpforo_ram_get( [ $this, '_get_forum' ], $args );
539 }
540
541 function get_forums( $args = [], &$items_count = 0, $count = false ) {
542 $cache = WPF()->cache->on('forum');
543
544 $default = [
545 'include' => [], // array( 2, 10, 25 )
546 'exclude' => [], // array( 2, 10, 25 )
547 'parent_include' => [], // array( 2, 10, 25 )
548 'parent_exclude' => [], // array( 2, 10, 25 )
549 'parentid' => null,
550 'parent_slug' => '',
551 'status' => null,
552 'type' => 'all', // category, forum
553 'orderby' => 'order', // order by `field`
554 'order' => 'ASC', // ASC DESC
555 'offset' => null, // OFFSET
556 'row_count' => null, // ROW COUNT
557 'layout' => null, // 1, 2, 3, 4
558 ];
559
560 $args = wpforo_parse_args( $args, $default );
561 extract( $args, EXTR_OVERWRITE );
562
563 $include = wpforo_parse_args( $include );
564 $exclude = wpforo_parse_args( $exclude );
565 $parent_include = wpforo_parse_args( $parent_include );
566 $parent_exclude = wpforo_parse_args( $parent_exclude );
567
568 $sql = "SELECT * FROM `" . WPF()->tables->forums . "`";
569 $wheres = [];
570
571 if( ! empty( $include ) ) $wheres[] = "`forumid` IN(" . implode( ', ', array_map( 'intval', $include ) ) . ")";
572 if( ! empty( $exclude ) ) $wheres[] = "`forumid` NOT IN(" . implode( ', ', array_map( 'intval', $exclude ) ) . ")";
573 if( ! empty( $parent_include ) ) $wheres[] = "`parentid` IN(" . implode( ', ', array_map( 'intval', $parent_include ) ) . ")";
574 if( ! empty( $parent_exclude ) ) $wheres[] = "`parentid` NOT IN(" . implode( ', ', array_map( 'intval', $parent_exclude ) ) . ")";
575 if( $parentid != null ) $wheres[] = " `parentid` = " . intval( $parentid );
576 if( $layout != null ) $wheres[] = " `layout` = " . intval( $layout );
577 if( $status != null ) $wheres[] = " `status` = " . intval( $status );
578
579 if( $type === 'category' ) {
580 $wheres[] = " `is_cat` = 1";
581 } elseif( $type === 'forum' ) {
582 $wheres[] = " `is_cat` = 0";
583 }
584
585 if( $parent_slug != '' ) $wheres[] = "`slug` = '" . esc_sql( $parent_slug ) . "'";
586
587 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
588
589 if( $count ) {
590 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
591 // $item_count_sql = preg_replace('#ORDER.+$#is', '', $item_count_sql);
592 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
593 }
594
595 $sql .= esc_sql( " ORDER BY `$orderby` " . $order );
596
597 if( $row_count != null ) {
598 if( $offset != null ) {
599 $sql .= esc_sql( " LIMIT $offset,$row_count" );
600 } else {
601 $sql .= esc_sql( " LIMIT $row_count" );
602 }
603 }
604
605 if( $cache ) {
606 $object_key = md5( $sql . WPF()->current_user_groupid );
607 $object_cache = WPF()->cache->get( $object_key );
608 if( ! empty( $object_cache ) ) {
609 $items_count = $object_cache['items_count'];
610
611 return $object_cache['items'];
612 }
613 }
614
615 $forums = WPF()->db->get_results( $sql, ARRAY_A );
616
617 $forums = array_map( function( $forum ){
618 if( $forum['cover'] = wpforo_bigintval( wpfval($forum, 'cover') ) ){
619 $image = wp_get_attachment_image_src( $forum['cover'], 'full' );
620 $forum['cover_url'] = (string) wpfval($image, 0);
621 }else{
622 $forum['cover_url'] = '';
623 }
624
625 return $forum;
626 }, $forums );
627
628 $forums = apply_filters( 'wpforo_get_forums', $forums, $args );
629
630 if( $cache && isset( $object_key ) && ! empty( $forums ) ) {
631 self::$cache['forums'][ $object_key ]['items'] = $forums;
632 self::$cache['forums'][ $object_key ]['items_count'] = $items_count;
633 }
634
635 return $forums;
636 }
637
638 function search( $needle, $fields = [] ) {
639
640 if( $needle ) {
641 $needle = sanitize_text_field( $needle );
642 if( empty( $fields ) ) {
643 $fields = [
644 'title',
645 'description',
646 'meta_key',
647 'meta_desc',
648 ];
649 }
650
651 $sql = "SELECT `forumid` FROM `" . WPF()->tables->forums . "`";
652 $wheres = [];
653
654 foreach( $fields as $field ) {
655 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $needle ) . "%'";
656 }
657
658 $sql .= " WHERE " . implode( " OR ", $wheres );
659
660 return WPF()->db->get_col( $sql );
661 }
662
663 return [];
664 }
665
666 function update_hierarchy() {
667 if( is_array( $_REQUEST['forum'] ) && ! empty( $_REQUEST['forum'] ) ) {
668 $i = 0;
669 foreach( $_REQUEST['forum'] as $hierarchy ) {
670
671 extract( $hierarchy );
672
673 if( ! isset( $forumid ) || ! $forumid = intval( $forumid ) ) continue;
674
675 if( false !== WPF()->db->update( WPF()->tables->forums, [
676 'parentid' => ( isset( $parentid ) ? intval( $parentid ) : 0 ),
677 'order' => ( isset( $order ) ? intval( $order ) : 0 ),
678 ], [ 'forumid' => intval( $forumid ) ], [
679 '%d',
680 '%d',
681 ], [ '%d' ] ) ) {
682 $i ++;
683 }
684
685 if( isset( $parentid ) && $parentid = intval( $parentid ) ) {
686 $layout = WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $parentid ) );
687 WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `layout` = " . intval( $layout ) . " WHERE `forumid` = " . intval( $forumid ) );
688 }
689
690 }
691
692 WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `is_cat` = 0" );
693 WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `is_cat` = 1 WHERE `parentid` = 0" );
694
695 if( $i ) {
696 $this->delete_tree_cache();
697 WPF()->notice->add( 'Forum hierarchy successfully updated', 'success' );
698 } else {
699 WPF()->notice->add( 'Cannot update forum hierarchy', 'error' );
700 }
701
702 }
703 }
704
705 /**
706 * @param int $forumid
707 *
708 * @return array array of child forumids
709 */
710 public function get_childs( $forumid ) {
711 $key = [ 'forums', 'get_childs', $forumid ];
712 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
713
714 if( wpforo_is_db_mysql8() ) {
715 $forumids = $this->__get_childs_mysql8( $forumid );
716 } else {
717 $forumids = $this->__get_childs( $forumid );
718 }
719
720 WPF()->ram_cache->set( $key, $forumids );
721
722 return $forumids;
723 }
724
725 /**
726 * @param int $forumid
727 *
728 * @return array array of child forumids
729 */
730 private function __get_childs( $forumid ) {
731 if( $forumid = intval( $forumid ) ) {
732 $sql = "SELECT GROUP_CONCAT(
733 @id := (
734 SELECT GROUP_CONCAT(`forumid` ORDER BY `order` ASC)
735 FROM `" . WPF()->tables->forums . "`
736 WHERE FIND_IN_SET( `parentid`, @id )
737 )
738 ) AS forumids
739 FROM ( SELECT @id := %s ) vars
740 STRAIGHT_JOIN `" . WPF()->tables->forums . "`
741 WHERE @id IS NOT NULL";
742 $sql = WPF()->db->prepare( $sql, $forumid );
743
744 $forumids = explode( ',', (string) WPF()->db->get_var( $sql ) );
745
746 return array_values( array_unique( array_filter( array_map( 'intval', $forumids ) ) ) );
747 }
748
749 return [];
750 }
751
752 /**
753 * @param int $forumid
754 *
755 * @return array array of child forumids
756 */
757 private function __get_childs_mysql8( $forumid ) {
758 if( $forumid = intval( $forumid ) ) {
759 $sql = "WITH RECURSIVE `forum_path` AS (
760 SELECT `forumid`, `parentid`
761 FROM `" . WPF()->tables->forums . "`
762 WHERE `parentid` = %d
763 UNION
764 SELECT f.`forumid`, f.`parentid`
765 FROM `" . WPF()->tables->forums . "` AS f
766 INNER JOIN `forum_path` AS fp ON fp.`parentid` <> fp.`forumid` AND fp.`forumid` = f.`parentid`
767 ) SELECT * FROM `forum_path`";
768 $sql = WPF()->db->prepare( $sql, $forumid );
769
770 $forumids = WPF()->db->get_col( $sql );
771
772 return array_values( array_unique( array_filter( array_map( 'intval', $forumids ) ) ) );
773 }
774
775 return [];
776 }
777
778 // get forums tree for drop down menu
779
780 /**
781 * Returns depth for this item.
782 *
783 * @param int $forumid
784 *
785 * @param int $depth
786 *
787 * @since 1.0.0
788 *
789 */
790 function count_depth( $forumid, &$depth ) {
791 if( wpforo_is_db_mysql8() ) {
792 $depth = $this->__count_depth_mysql8( $forumid );
793
794 return;
795 }
796
797 $sql = "SELECT `parentid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = %d AND `parentid` <> %d";
798 $sql = WPF()->db->prepare( $sql, intval( $forumid ), intval( $forumid ) );
799
800 if( WPF()->ram_cache->exists( $sql ) ) {
801 $parentid = WPF()->ram_cache->get( $sql );
802 } else {
803 $parentid = WPF()->db->get_var( $sql );
804 WPF()->ram_cache->set( $sql, $parentid );
805 }
806
807 if( $parentid ) {
808 $depth ++;
809 $this->count_depth( $parentid, $depth );
810 }
811 }
812
813 private function __count_depth_mysql8( $forumid ) {
814 $sql = "WITH RECURSIVE `forum_path` AS(
815 SELECT `forumid`, `parentid`, 0 AS `depth`
816 FROM `" . WPF()->tables->forums . "`
817 WHERE `forumid` = %d
818 UNION
819 SELECT f.`forumid`, f.`parentid`, `depth` + 1
820 FROM `" . WPF()->tables->forums . "` f
821 INNER JOIN `forum_path` fp ON fp.`parentid` <> fp.`forumid` AND fp.`parentid` = f.`forumid`
822 ) SELECT `depth` FROM `forum_path` ORDER BY `depth` DESC LIMIT 1";
823
824 return (int) WPF()->db->get_var( WPF()->db->prepare( $sql, intval( $forumid ) ) );
825 }
826
827 /**
828 * @param int $forumid
829 *
830 * @return array
831 */
832 function get_child_forums( $forumid ) {
833 $sql = "SELECT `forumid`
834 FROM `" . WPF()->tables->forums . "`
835 WHERE `parentid` = " . intval( $forumid ) . "
836 AND `forumid` <> " . intval( $forumid ) . "
837 ORDER BY `order`";
838
839 return array_map( 'intval', WPF()->db->get_col( $sql ) );
840 }
841
842 function forum_list( $forumids, $type = 'select_box', $selected = [], $cats = true, $disabled = [] ) {
843 static $old_depth;
844 $disabled = (array) $disabled;
845 $selected = (array) $selected;
846
847 foreach( $forumids as $forumid ) {
848 $forumid = intval( $forumid );
849 if( ! $forumid || ( ! wpforo_is_admin() && ! WPF()->perm->forum_can( 'vf', $forumid ) ) || ( wpforo_is_admin() && ! WPF()->usergroup->can_manage_forum() ) ) continue;
850 if( $type === 'subscribe_manager_form' && ! WPF()->perm->forum_can( 'sb', $forumid ) ) continue;
851 $depth = 0;
852 $this->count_depth( $forumid, $depth );
853 $forum = wpforo_forum( $forumid );
854 $name = wpfval($forum, 'title');
855 if( $type === 'select_box' ) { ?>
856 <option value="<?php echo $forumid ?>" <?php echo( ( ! $cats && $depth == 0 || ( in_array( $forumid, $disabled ) ) ) ? ' disabled ' : '' );
857 echo( in_array( $forumid, $selected ) ? ' selected ' : '' ) ?> > <?php echo esc_html( str_repeat( '— ', $depth ) . trim( (string) $name ) ) ?></option><?php
858 } elseif( $type === 'drag_menu' ) {
859 switch( $forum['layout'] ) {
860 case 2:
861 $layout_name = 'Simplified Layout';
862 break;
863 case 3:
864 $layout_name = 'Q&A Layout';
865 break;
866 case 4:
867 $layout_name = 'Threaded Layout';
868 break;
869 default:
870 $layout_name = 'Extended Layout';
871 }
872 ?>
873
874 <li id="menu-item-<?php echo $forumid ?>" class="menu-item menu-item-depth-<?php echo esc_attr( $depth ) ?>">
875 <input id="forumid-<?php echo $forumid ?>" type="hidden" name="forum[<?php echo $forumid ?>][forumid]"/>
876 <input id="parentid-<?php echo $forumid ?>" type="hidden" name="forum[<?php echo $forumid ?>][parentid]"/>
877 <input id="order-<?php echo $forumid ?>" type="hidden" name="forum[<?php echo $forumid ?>][order]"/>
878 <dl class="menu-item-bar">
879 <dt class="menu-item-handle forum_width">
880 <span class="item-title forumtitle"><span style="font-weight:400; cursor:help;" title="Forum ID"><?php echo $forumid; ?> &nbsp;|&nbsp;</span> <?php echo apply_filters( 'wpforo_dashboard_forums_item_name', esc_html($name), $forumid); ?></span>
881 <span class="item-controls">
882 <span class="wpforo-cat-layout"><?php echo( $depth != 0 ? __( 'Topics', 'wpforo' ) . '&nbsp;(' . intval( $forum['topics'] ) . ')&nbsp;,&nbsp;' . __( 'Posts', 'wpforo' ) . '&nbsp;(' . intval( $forum['posts'] ) . ')&nbsp; | &nbsp;' : '' ) ?><?php echo( $depth == 0 ? '(&nbsp;<i>' . esc_html( $layout_name ) . '</i>&nbsp;)&nbsp; | &nbsp;' : '' ); ?></span>
883 <span class="menu_add">
884 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add&parentid=' . $forumid ) ?>">
885 <span class="dashicons dashicons-plus" title="<?php if( $depth ) : _e( 'Add a new Subforum', 'wpforo' ); else: _e( 'Add a new Forum in this Category', 'wpforo' ); endif; ?>"></span>
886 </a>
887 </span> &nbsp;|&nbsp;
888 <span class="menu_copy">
889 <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&wpfaction=forum_copy&forumid=' . $forumid ), 'forum_copy', '_wpfnonce' ) ?>" onclick="return confirm( '<?php wpforo_phrase( 'Are you sure you want to copy this forum?' ) ?>' )">
890 <span class="dashicons dashicons-admin-page" title="<?php _e( 'Copy', 'wpforo' ); ?>"></span>
891 </a>
892 </span> &nbsp;|&nbsp;
893 <span class="menu_edit">
894 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&id=' . $forumid . '&action=edit' ) ?>">
895 <span class="dashicons dashicons-edit" title="<?php _e( 'edit', 'wpforo' ) ?>"></span>
896 </a>
897 </span>&nbsp;|&nbsp;
898 <?php if( WPF()->usergroup->can_manage_forum() ): ?>
899 <span class="menu_delete">
900 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&id=' . $forumid . '&action=del' ) ?>">
901 <span class="dashicons dashicons-trash" title="<?php _e( 'delete', 'wpforo' ) ?>"></span>
902 </a>
903 </span>&nbsp;|&nbsp;
904 <?php endif; ?>
905 <span class="menu_view">
906 <a href="<?php echo esc_url( (string) wpforo_forum( $forumid, 'url' ) ); ?>">
907 <span class="dashicons dashicons-visibility" title="<?php _e( 'View', 'wpforo' ) ?>"></span>
908 </a>
909 </span>
910
911 </span>
912 </dt>
913 </dl>
914 <ul class="menu-item-transport"></ul>
915 </li>
916
917 <?php
918 } elseif( $type === 'front_list' ) {
919 if( isset( $old_depth ) && $old_depth == $depth ) echo '</dd><dd>';
920 if( isset( $old_depth ) && $old_depth < $depth ) echo '<dl><dd>';
921 if( isset( $old_depth ) && $old_depth > $depth ) echo '</dd></dl>';
922 $old_depth = $depth;
923 printf(
924 '<span class="wpf-dl-item %1$s"><a href="%2$s"><span class="wpf-circle wpf-s" style="display: inline-flex; %3$s"><i class="%4$s"></i></span><span class="wpf-dl-item-label">%5$s</span></a></span>',
925 ( in_array( $forumid, $selected ) ? 'wpf-dl-current' : '' ),
926 esc_url( (string) wpforo_forum( $forumid, 'url' ) ),
927 ( ($forum['color'] = trim( (string) $forum['color'] )) ? 'color: ' . $forum['color'] . ';' : ''),
928 $forum['icon'],
929 esc_html( $name )
930 );
931 } elseif( $type === 'subscribe_manager_form' ) {
932 ?>
933 <li>
934 <?php if( $depth > 0 ) :
935 $forum_topic_attr = '';
936 $forum_attr = '';
937 if( key_exists( $forumid, $selected ) ) {
938 if( $selected[ $forumid ] === 'forum-topic' ) {
939 $forum_topic_attr = ' checked ';
940 } elseif( $selected[ $forumid ] === 'forum' ) {
941 $forum_attr = ' checked ';
942 }
943 }
944 ?>
945 <div class="wpf-sbs-div wpf-sbs-checkbox">
946 <input id="wpf_sbs_allposts_<?php echo $forumid ?>" type="checkbox" name="wpforo[forums][<?php echo $forumid ?>]" value="forum-topic" <?php echo $forum_topic_attr ?>><label class="wpf-sbsp" for="wpf_sbs_allposts_<?php echo $forumid ?>"><?php wpforo_phrase( 'topics and posts' ) ?></label>
947 <input id="wpf_sbs_alltopics_<?php echo $forumid ?>" type="checkbox" name="wpforo[forums][<?php echo $forumid ?>]" value="forum" <?php echo $forum_attr ?>><label class="wpf-sbst" for="wpf_sbs_alltopics_<?php echo $forumid ?>"><?php wpforo_phrase( 'topics' ) ?></label>
948 </div>
949 <?php endif; ?>
950 <div class="wpf-sbs-div wpf-sbs-form-title<?php echo ( $depth > 0 ) ? ' wpf-sbs-forum' : ' wpf-sbs-cat'; ?>"><?php echo esc_html( str_repeat( '— ', $depth ) ) . trim( (string) $name ) ?></div>
951 </li>
952 <?php
953 }
954 $subforums = $this->get_child_forums( $forumid );
955 if( ! empty( $subforums ) ) {
956 $this->forum_list( $subforums, $type, $selected, true, $disabled );
957 }
958 }
959 }
960
961 function tree( $type = 'select_box', $cats = true, $selected = [], $cache = true, $disabled = [], $parentids = [] ) {
962 $disabled = (array) $disabled;
963 $selected = (array) $selected;
964 $parentids = (array) $parentids;
965 if( ! $parentids ) $parentids = WPF()->db->get_col( "SELECT `forumid` FROM `" . WPF()->tables->forums . "` WHERE `parentid` = 0 ORDER BY `order`" );
966 if( ! empty( $parentids ) ) {
967 if( $cache && ! wpforo_is_admin() ) {
968 $key = md5( serialize( $parentids ) . $type . (int) $cats . WPF()->current_user_groupid );
969 $html = wpforo_get_option( 'forum_tree_' . $key, '' );
970 $pattern_strip_selected = '#(<(?:option|input)[^<>]*?)[\r\n\t\s]*(?:selected|checked)[^\r\n\t\s]*?((?:[\r\n\t\s][^<>]*)?>)#isu';
971
972 if( $html ) {
973 if( $type === 'select_box' || $type === 'subscribe_manager_form' ) $html = preg_replace( $pattern_strip_selected, '$1$2', (string) $html );
974 if( $selected ) {
975 if( $type === 'select_box' ) {
976 foreach( $selected as $sfid ) {
977 $html = str_replace( 'value="' . $sfid . '"', 'value="' . $sfid . '" selected ', $html );
978 }
979 } elseif( $type === 'subscribe_manager_form' ) {
980 foreach( $selected as $forumid => $stype ) {
981 $html = preg_replace( '#(name=[\'"]wpforo\[forums\]\[' . intval( $forumid ) . '\][\'"][^<>]*?value=[\'"]' . preg_quote( $stype ) . '[\'"]|value=[\'"]' . preg_quote( $stype ) . '[\'"][^<>]*?name=[\'"]wpforo\[forums\]\[' . intval( $forumid ) . '\][\'"])#isu', '$1 checked', (string) $html );
982 }
983 }
984 }
985 echo $html;
986 } elseif( function_exists( 'ob_start' ) ) {
987 ob_start();
988 $this->forum_list( $parentids, $type, $selected, $cats, $disabled );
989 $html = ob_get_clean();
990 $cache_html = ( $type === 'select_box' ? preg_replace( $pattern_strip_selected, '$1$2', (string) $html ) : $html );
991 if( $type !== 'drag_menu' ) wpforo_update_option( 'forum_tree_' . $key, $cache_html );
992 echo $html;
993 }
994 } else {
995 $this->forum_list( $parentids, $type, $selected, $cats, $disabled );
996 }
997 }
998 }
999
1000 public function delete_tree_cache() {
1001 WPF()->db->query( "DELETE FROM `" . WPF()->db->options . "` WHERE `option_name` REGEXP '^" . wpforo_prefix( 'forum_tree_' ) . "'" );
1002 }
1003
1004 function parentid( $topicid = 0 ) {
1005 if( isset( $_GET['page'] ) && preg_match( '#^wpforo-(?:\d+-)?forums$#iu', (string) $_GET['page'] ) ) {
1006 if( isset( $_GET['id'] ) ) return WPF()->db->get_var( "SELECT `parentid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $_GET['id'] ) );
1007 } elseif( isset( $_GET['page'] ) && preg_match( '#^wpforo-(?:\d+-)?topics$#iu', (string) $_GET['page'] ) ) {
1008 if( isset( $_GET['id'] ) ) return WPF()->db->get_var( "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . wpforo_bigintval( $_GET['id'] ) );
1009 } else {
1010 if( $topicid ) return WPF()->db->get_var( "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . wpforo_bigintval( $topicid ) );
1011 }
1012 }
1013
1014 function permissions() {
1015 $access_arr = WPF()->perm->get_accesses();
1016 if( ! empty( $access_arr ) ) {
1017
1018 if( isset( $_GET['id'] ) ) {
1019 if( $permissions_srlz = WPF()->db->get_var( "SELECT `permissions` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $_GET['id'] ) ) ) {
1020 $permissions_arr = unserialize( $permissions_srlz );
1021 }
1022 }
1023
1024 if( $usergroups = WPF()->db->get_results( "SELECT `groupid`, `name` FROM `" . WPF()->tables->usergroups . "`", ARRAY_A ) ) {
1025 foreach( $usergroups as $usergroup ) {
1026 extract( $usergroup, EXTR_OVERWRITE );
1027 echo '
1028 <tr>
1029 <td>' . esc_html( $name ) . '</td>
1030 <td>
1031 <select name="forum[permission][' . intval( $groupid ) . ']">';
1032 foreach( $access_arr as $value ) {
1033
1034 echo '<option value="' . esc_attr(
1035 $value['access']
1036 ) . '" ' . ( ( isset( $permissions_arr[ $groupid ] ) && $value['access'] == $permissions_arr[ $groupid ] ) || ( ! isset( $permissions_arr[ $groupid ] ) && ( ( $name == 'Guest' && $value['access'] == 'read_only' ) || ( $name == 'Registered' && $value['access'] == 'standard' ) || ( $name == 'Customer' && $value['access'] == 'standard' ) || ( $name == 'Moderator' && $value['access'] == 'moderator' ) || ( $name == 'Admin' && $value['access'] == 'full' ) || ( $name != 'Guest' && $name != 'Registered' && $name != 'Customer' && $name != 'Moderator' && $name != 'Admin' && $value['access'] == 'standard' ) ) ) ? ' selected ' : '' ) . '>' . esc_html(
1037 __( $value['title'], 'wpforo' )
1038 ) . '</option>';
1039 }
1040 echo '
1041 </select>
1042 </td>
1043 </tr>
1044 ';
1045 }
1046
1047
1048 }
1049 }
1050 }
1051
1052 /**
1053 * array get_counts(array or id(num))
1054 *
1055 * @param array defined arguments array for returning
1056 *
1057 * @return array array('topics' => 0, 'posts' => 0)
1058 * @since 1.0.0
1059 *
1060 */
1061 function get_counts( $forumids ): array {
1062 $result = [ 'topics' => 0, 'posts' => 0 ];
1063 $forumids = array_filter( array_map( 'intval', (array) $forumids ) );
1064 if( $forumids ) {
1065 $sql = "SELECT SUM(`topics`) as topics, SUM(`posts`) as posts FROM `" . WPF()->tables->forums . "` WHERE `forumid` IN(" . implode( ',', $forumids ) . ")";
1066 $row = (array) WPF()->db->get_row( $sql, ARRAY_A );
1067 $result['topics'] = (int) wpfval( $row, 'topics' );
1068 $result['posts'] = (int) wpfval( $row, 'posts' );
1069 }
1070
1071 return $result;
1072 }
1073
1074 /**
1075 * array get_layout(array)
1076 *
1077 * @param array defined arguments array for returning
1078 *
1079 * @return int layout id
1080 * @since 1.0.0
1081 *
1082 */
1083 function get_layout( $args = null ) {
1084 if( is_null( $args ) ) $args = WPF()->current_object['forum'];
1085 if( ! $args ) return 1;
1086 if( $layout = (int) wpfval( $args, 'layout' ) ) return $layout;
1087 if( is_array( $args ) ) {
1088 $default = [
1089 'forumid' => null, // forum id
1090 'topicid' => null, // topic id
1091 'postid' => null, // post id
1092 ];
1093 } else {
1094 $default = [
1095 'forumid' => $args, // forumid
1096 'topicid' => null, // topic id
1097 'postid' => null, // post id
1098 ];
1099 }
1100 $args = wpforo_parse_args( $args, $default );
1101 if( ! empty( $args ) ) {
1102 extract( $args, EXTR_OVERWRITE );
1103
1104 if( $args['forumid'] ) {
1105 $layout = (int) wpforo_forum( $args['forumid'], 'layout' );
1106
1107 return ( $layout ?: 1 );
1108 } elseif( $args['topicid'] ) {
1109 $sql = "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . intval( $args['topicid'] );
1110 $forumid = WPF()->db->get_var( $sql );
1111
1112 return $this->get_layout( [ 'forumid' => $forumid ] );
1113 } elseif( $args['postid'] ) {
1114 $sql = "SELECT `forumid` FROM `" . WPF()->tables->posts . "` WHERE `postid` = " . intval( $args['postid'] );
1115 $forumid = WPF()->db->get_var( $sql );
1116
1117 return $this->get_layout( [ 'forumid' => $forumid ] );
1118 }
1119 }
1120
1121 return 1;
1122 }
1123
1124 function get_forum_url( $forum ) {
1125
1126 if( ! is_array( $forum ) ) {
1127 if( is_numeric( $forum ) ) {
1128 $forum = $this->get_forum( $forum );
1129 } else {
1130 $forum = [ 'slug' => $forum ];
1131 }
1132 }
1133
1134 if( is_array( $forum ) && ! empty( $forum ) ) {
1135 return wpforo_home_url( utf8_uri_encode( $forum['slug'] ) );
1136 } else {
1137 return wpforo_home_url();
1138 }
1139 }
1140
1141 function get_parents( $forumid, &$relative_ids ) {
1142 if( wpforo_is_db_mysql8() ) {
1143 $relative_ids = $this->__get_parents_mysql8( $forumid );
1144
1145 return;
1146 }
1147
1148 $sql = "SELECT `parentid`, `forumid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = %d";
1149 $sql = WPF()->db->prepare( $sql, $forumid );
1150 if( WPF()->ram_cache->exists( $sql ) ) {
1151 $forum = WPF()->ram_cache->get( $sql );
1152 } else {
1153 $forum = WPF()->db->get_row( $sql, ARRAY_A );
1154 WPF()->ram_cache->set( $sql, $forum );
1155 }
1156 if( $forum ) {
1157 $relative_ids[] = $forum['forumid'];
1158 if( $forum['parentid'] ) {
1159 $this->get_parents( $forum['parentid'], $relative_ids );
1160 } else {
1161 $relative_ids = array_reverse( $relative_ids );
1162 }
1163 }
1164 }
1165
1166 /**
1167 * @param int $forumid
1168 *
1169 * @return array
1170 */
1171 private function __get_parents_mysql8( $forumid ) {
1172 $sql = "WITH RECURSIVE `forum_path` AS(
1173 SELECT `forumid`, `parentid`, 0 AS `depth`
1174 FROM `" . WPF()->tables->forums . "`
1175 WHERE `forumid` = %d
1176 UNION
1177 SELECT f.`forumid`, f.`parentid`, `depth` + 1
1178 FROM `" . WPF()->tables->forums . "` f
1179 INNER JOIN `forum_path` fp ON fp.`parentid` <> fp.`forumid` AND fp.`parentid` = f.`forumid`
1180 ) SELECT `forumid` FROM `forum_path` ORDER BY `depth` DESC";
1181 $sql = WPF()->db->prepare( $sql, intval( $forumid ) );
1182 if( WPF()->ram_cache->exists( $sql ) ) {
1183 $relative_ids = WPF()->ram_cache->get( $sql );
1184 } else {
1185 $relative_ids = (array) WPF()->db->get_col( $sql );
1186 WPF()->ram_cache->set( $sql, $relative_ids );
1187 }
1188
1189 return $relative_ids;
1190 }
1191
1192 function get_count( $args = [] ) {
1193 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->forums . "`";
1194 if( ! empty( $args ) ) {
1195 $wheres = [];
1196 foreach( $args as $key => $value ) $wheres[] = "`$key` = " . intval( $value );
1197 if( $wheres ) $sql .= " WHERE " . implode( ' AND ', $wheres );
1198 }
1199
1200 return WPF()->db->get_var( $sql );
1201 }
1202
1203 function get_lastinfo( $ids = [] ) {
1204 $lastinfo = [];
1205 if( ! empty( $ids ) ) {
1206 $ids = implode( ',', array_map( 'intval', $ids ) );
1207 $lastinfo = WPF()->db->get_row( "SELECT `userid` as last_userid, `topicid` as last_topicid, `postid` as last_postid, `created` as last_post_date FROM `" . WPF()->tables->posts . "` WHERE `status` = 0 AND `private` = 0 AND forumid IN(" . $ids . ") ORDER BY `created` DESC LIMIT 1", ARRAY_A );
1208 }
1209
1210 return $lastinfo;
1211 }
1212
1213 function forums() {
1214 $forums = $this->get_forums( [ 'parentid' => 0 ] );
1215
1216 return $this->children( $forums );
1217 }
1218
1219 function children( $forums, $parentId = 0, $level = 0 ) {
1220 if( empty( $forums ) || ! is_array( $forums ) ) return;
1221 $items = [];
1222 $level = $level + 1;
1223 foreach( $forums as $forum ) {
1224 if( ! isset( $forum['forumid'] ) || ! WPF()->perm->forum_can( 'vf', $forum['forumid'] ) ) continue;
1225 $forum['level'] = $level + 1;
1226 if( $forum['parentid'] == $parentId ) {
1227 $children = $this->children( $forums, $forum['forumid'], $level );
1228 if( $children ) {
1229 $forum['children'] = $children;
1230 }
1231 $items[] = $forum;
1232 }
1233 }
1234
1235 return $items;
1236 }
1237
1238 function dropdown( $forums = [] ) {
1239 if( empty( $forums ) ) {
1240 $forums = $this->forums();
1241 }
1242 foreach( $forums as $forum ) {
1243 if( isset( $forum['level'] ) ) $forum['level'] = $forum['level'] - 2;
1244 $prefix = ( $forum['level'] == 0 ) ? '' : str_repeat( '&mdash;', $forum['level'] );
1245 echo '<option value="' . esc_attr( $forum['forumid'] ) . '"> ' . $prefix . '&nbsp;' . esc_html( $forum['title'] ) . '</option>';
1246 if( ! empty( $forum['children'] ) ) {
1247 $this->dropdown( $forum['children'] );
1248 }
1249 }
1250 }
1251
1252 function private_forum( $forumid, $groupids = [] ) {
1253 if( $forumid = intval( $forumid ) ) {
1254 if( ! $groupids ) $groupids = WPF()->current_user_groupids;
1255 if( ($groupids = array_map( 'intval', (array) $groupids)) && !WPF()->perm->forum_can( 'vf', $forumid, $groupids ) ) return true;
1256 }
1257
1258 return false;
1259 }
1260
1261 public function get_forumid_and_childids( int $forumid, bool $check_permission = true ): array {
1262 $forumids = array_merge( [$forumid], $this->get_childs( $forumid ) );
1263
1264 if( $check_permission ) $forumids = array_filter( $forumids, function( $forumid ){
1265 return WPF()->perm->forum_can( 'vf', $forumid );
1266 } );
1267
1268 return $forumids;
1269 }
1270
1271 public function get_sum_of_topics_posts( int $forumid, bool $check_permission = true ): array {
1272 $forumids = $this->get_forumid_and_childids( $forumid, $check_permission );
1273
1274 return $this->get_counts( $forumids );
1275 }
1276
1277 public function after_add_usergroup( $group ) {
1278 if( $group['groupid'] ) {
1279 if( $forums = $this->get_forums() ) {
1280 foreach( $forums as $forum ) {
1281 if( $permissions = (string) wpfval( $forum, 'permissions' ) ) {
1282 if( $permissions = maybe_unserialize( $permissions ) ) {
1283 $permissions[ $group['groupid'] ] = $group['access'];
1284 WPF()->db->update( WPF()->tables->forums, [ 'permissions' => serialize( $permissions ) ], [ 'forumid' => $forum['forumid'] ], [ '%s' ], [ '%d' ] );
1285 }
1286 }
1287 }
1288 }
1289 }
1290 }
1291
1292 public function after_merge_forum( $forumid, $mergeid ) {
1293 $this->rebuild_last_infos( $mergeid );
1294 $this->rebuild_stats( $mergeid );
1295 }
1296
1297 public function after_add_topic( $topic ) {
1298 if( !intval($topic['status']) && !intval($topic['private']) ){
1299 $this->rebuild_last_infos( $topic['forumid'] );
1300 $this->rebuild_stats( $topic['forumid'] );
1301 }
1302 }
1303
1304 public function after_delete_topic( $topic ) {
1305 if( !intval($topic['status']) && !intval($topic['private']) ){
1306 $this->rebuild_last_infos( $topic['forumid'] );
1307 $this->rebuild_stats( $topic['forumid'] );
1308 }
1309 }
1310
1311 public function after_topic_status_update( $topic ) {
1312 if( !intval($topic['private']) ){
1313 $this->rebuild_last_infos( $topic['forumid'] );
1314 $this->rebuild_stats( $topic['forumid'] );
1315 }
1316 }
1317
1318 public function topic_private_update( $topicid ) {
1319 if( $topic = WPF()->topic->get_topic( $topicid ) ){
1320 $this->rebuild_last_infos( $topic['forumid'] );
1321 $this->rebuild_stats( $topic['forumid'] );
1322 }
1323 }
1324
1325 public function after_merge_topic( $target, $current ) {
1326 $this->rebuild_last_infos( $target['forumid'] );
1327 $this->rebuild_stats( $target['forumid'] );
1328 $this->rebuild_last_infos( $current['forumid'] );
1329 $this->rebuild_stats( $current['forumid'] );
1330 }
1331
1332 public function after_move_topic( $topic, $forumid ) {
1333 $this->rebuild_last_infos( $topic['forumid'] );
1334 $this->rebuild_stats( $topic['forumid'] );
1335 $this->rebuild_last_infos( $forumid );
1336 $this->rebuild_stats( $forumid );
1337 }
1338
1339 public function after_add_post( $post, $topic, $forum ) {
1340 if( !intval( $post['status'] ) ){
1341 $this->rebuild_last_infos( $forum['forumid'] );
1342 $this->rebuild_stats( $forum['forumid'] );
1343 }
1344 }
1345
1346 public function after_delete_post( $post ) {
1347 if( !intval( $post['status'] ) ){
1348 $this->rebuild_last_infos( $post['forumid'] );
1349 $this->rebuild_stats( $post['forumid'] );
1350 }
1351 }
1352
1353 public function after_post_status_update( $post ) {
1354 if( !intval( $post['private'] ) ){
1355 $this->rebuild_last_infos( $post['forumid'] );
1356 $this->rebuild_stats( $post['forumid'] );
1357 }
1358 }
1359 }
1360