PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.1
Forumax – AI Powered Advanced Community Forum Plugin v2.5.1
2.5.1 2.5.0 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 All 31 releases
← All changes | includes/admin/menu/admin_ui.php +244 -171 1.3.0 → 2.5.1 View file →
@@ -1,172 +1,245 @@
1 -<?php
2 -if ( ! class_exists( 'bbPress' ) ) {
3 - return;
4 -}
5 -
6 -$parent_forums = [];
7 -$fcount = wp_count_posts( bbp_get_forum_post_type() );
8 -$forum_count = (int) ( $fcount->publish + $fcount->hidden + $fcount->spam );
9 -$bbpc_opt = get_option( 'bbp_core_settings' );
10 -?>
11 -<div class="wrap">
12 -<div class="body-dark">
13 - <?php
14 - if ( $forum_count > 0 ) :
15 - include __DIR__ . '/admin_ui/header.php';
16 - ?>
17 - <main>
18 - <div class="easydocs-sidebar-menu">
19 - <div class="tab-container">
20 - <?php
21 - $forum_query = new WP_Query( [
22 - 'post_type' => bbp_get_forum_post_type(),
23 - 'posts_per_page' => -1,
24 - 'post_parent' => 0,
25 - 'orderby' => 'menu_order',
26 - 'order' => 'ASC',
27 - 'post_status' => 'publish',
28 - ] );
29 - $count = $forum_query->found_posts;
30 - // Left Sidebar Forums.
31 - include __DIR__ . '/admin_ui/forums.php';
32 - ?>
33 - <div class="easydocs-tab-content">
34 - <?php
35 - $ids = 0;
36 - $container = 1;
37 - if ( is_array( $parent_forums ) ) :
38 - foreach ( $parent_forums as $item ) :
39 - $ids ++;
40 - $container ++;
41 - $active = $ids == 1 ? ' tab-active' : '';
42 -
43 - $children = new WP_Query(
44 - [
45 - 'post_parent' => $item,
46 - 'post_type' => bbp_get_topic_post_type(),
47 - 'orderby' => 'menu_order',
48 - 'order' => 'asc',
49 - 'posts_per_page' => -1,
50 - 'post_status' => [ 'any', 'spam' ],
51 - ]
52 - );
53 -
54 - $count_open = 0;
55 - $count_closed = 0;
56 - $count_hidden = 0;
57 - $count_no_reply = 0;
58 - $count_solved = 0;
59 - $count_unsolved = 0;
60 - $count_trash = 0;
61 -
62 - while ( $children->have_posts() ) : $children->the_post();
63 - $topic_id = get_the_ID();
64 - $replies = get_children( [
65 - 'post_parent' => $topic_id,
66 - 'post_type' => bbp_get_reply_post_type(),
67 - 'post_status' => [ 'publish', 'draft', 'pending' ],
68 - ] );
69 -
70 - // Count open/closed topics.
71 - if ( bbp_is_topic_closed( $topic_id ) ) {
72 - $count_closed++;
73 - } else {
74 - $count_open++;
75 - }
76 -
77 - // Count spam( hidden ) topics.
78 - if ( bbp_is_topic_spam( $topic_id ) || bbp_is_topic_pending( $topic_id ) ) {
79 - $count_hidden++;
80 - }
81 -
82 - // Replies count.
83 - if ( 0 == count( $replies ) ) {
84 - $count_no_reply++;
85 - }
86 -
87 - // Count solved.
88 - // Before using $GLOBALS['bbp_solved_topic'], check if it is set
89 - if (isset($GLOBALS['bbp_solved_topic']) && $GLOBALS['bbp_solved_topic']->is_solved($topic_id)) {
90 - $count_solved++;
91 - } else {
92 - $count_unsolved++;
93 - }
94 - endwhile;
95 - wp_reset_postdata();
96 -
97 - $trash_topic = new WP_Query( [
98 - 'post_parent' => $item,
99 - 'post_type' => bbp_get_topic_post_type(),
100 - 'posts_per_page' => -1,
101 - 'post_status' => [ 'trash' ],
102 - ] );
103 -
104 - while ( $trash_topic->have_posts() ) :
105 - $trash_topic->the_post();
106 - $trash_topic_id = get_the_ID();
107 - // Count trash.
108 - if ( bbp_is_topic_trash( $trash_topic_id ) ) {
109 - $count_trash++;
110 - }
111 - endwhile;
112 - wp_reset_postdata();
113 - ?>
114 - <div class="easydocs-tab <?php echo esc_attr( $active ); ?>" id="tab-<?php echo esc_attr( $item ); ?>">
115 -
116 - <!-- Tab filters. -->
117 - <?php include __DIR__ . '/admin_ui/tab_filters.php'; ?>
118 -
119 - <!-- Children topics. -->
120 - <?php include __DIR__ . '/admin_ui/topics.php'; ?>
121 -
122 - <a class="easydocs-btn easydocs-btn-outline-blue easydocs-btn-sm easydocs-btn-round button button-info section-doc" id="bbpc-topic" target="_blank" name="submit" href="javascript:void(0)" bbp_forum_id="<?php echo esc_attr( $item ); ?>">
123 - <?php esc_html_e( 'Add Topic', 'bbp-core' ); ?>
124 - </a>
125 - </div>
126 - <?php
127 - endforeach;
128 - endif;
129 - ?>
130 - </div>
131 - </div>
132 - </div>
133 - </main>
134 - <?php
135 - else :
136 - ?>
137 - <div class="eazydocs-no-content">
138 - <img src="<?php echo BBPC_IMG; ?>/icon/folder-open.png" alt="<?php esc_attr_e( 'Folder Open', 'bbp-core' ); ?>">
139 - <p class="big-p"> <?php esc_html_e( 'No forum has been found . Perhaps', 'bbp-core' ); ?> </p>
140 - <p> <br>
141 - <a href="javascript:void(0)" type="button" id="bbpc-forum" class="button button-primary ezd-btn btn-lg">
142 - <?php esc_html_e( 'Create Forum', 'bbp-core' ); ?>
143 - </a>
144 - </p>
145 - </div>
146 - <?php
147 - endif;
148 - ?>
149 - </div>
150 -</div>
151 -
152 -<script>
153 - (function ($) {
154 - $(document).ready(function () {
155 - let docContainer = document.querySelectorAll('.easydocs-tab');
156 - var config = {
157 - controls: {
158 - scope: 'local',
159 - },
160 - animation: {
161 - enable: false,
162 - },
163 - load: {
164 - filter: '<?php echo esc_js( $bbpc_opt['default_filter'] ?? '.open-topics' ); ?>'
165 - }
166 - };
167 - for (let i = 0; i < docContainer.length; i++) {
168 - var mixer1 = mixitup(docContainer[i], config);
169 - }
170 - });
171 -})(jQuery);
1 +<?php
2 +if ( ! class_exists( 'bbPress' ) ) {
3 + return;
4 +}
5 +
6 +if ( ! function_exists( 'forumax_admin_reply_stats' ) ) {
7 + /**
8 + * Reply counts and pending reply IDs for many topics in two queries.
9 + *
10 + * Replaces per-topic get_children()/WP_Query calls, which made this screen
11 + * run several queries per topic on large forums.
12 + *
13 + * @param int[] $topic_ids Topic IDs.
14 + * @return array{total: array<int,int>, pending: array<int,int[]>}
15 + */
16 + function forumax_admin_reply_stats( array $topic_ids ) {
17 + global $wpdb;
18 +
19 + $stats = [
20 + 'total' => [],
21 + 'pending' => [],
22 + ];
23 + $topic_ids = array_values( array_unique( array_filter( array_map( 'intval', $topic_ids ) ) ) );
24 +
25 + foreach ( array_chunk( $topic_ids, 500 ) as $chunk ) {
26 + $placeholders = implode( ',', array_fill( 0, count( $chunk ), '%d' ) );
27 + $args = array_merge( [ bbp_get_reply_post_type() ], $chunk );
28 +
29 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
30 + $rows = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_status FROM {$wpdb->posts} WHERE post_type = %s AND post_status IN ( 'publish', 'draft', 'pending' ) AND post_parent IN ( $placeholders ) ORDER BY post_date DESC", $args ) );
31 +
32 + foreach ( $rows as $row ) {
33 + $parent = (int) $row->post_parent;
34 + $stats['total'][ $parent ] = ( $stats['total'][ $parent ] ?? 0 ) + 1;
35 +
36 + if ( 'pending' === $row->post_status ) {
37 + $stats['pending'][ $parent ][] = (int) $row->ID;
38 + }
39 + }
40 + }
41 +
42 + // Pending reply excerpts and each topic's freshness link are rendered,
43 + // so load those posts together instead of one query each.
44 + $prime_ids = empty( $stats['pending'] ) ? [] : array_merge( ...array_values( $stats['pending'] ) );
45 + foreach ( $topic_ids as $topic_id ) {
46 + $prime_ids[] = (int) get_post_meta( $topic_id, '_bbp_last_active_id', true );
47 + }
48 + $prime_ids = array_filter( $prime_ids );
49 +
50 + if ( ! empty( $prime_ids ) ) {
51 + _prime_post_caches( $prime_ids, false, true );
52 + }
53 +
54 + return $stats;
55 + }
56 +}
57 +
58 +$parent_forums = [];
59 +$fcount = wp_count_posts( bbp_get_forum_post_type() );
60 +$forum_count = (int) ( $fcount->publish + $fcount->hidden + $fcount->spam );
61 +?>
62 +<div class="wrap">
63 +<div class="body-dark">
64 + <?php
65 + if ( $forum_count > 0 ) :
66 + include __DIR__ . '/admin_ui/header.php';
67 + ?>
68 + <main>
69 + <div class="easydocs-sidebar-menu">
70 + <div class="tab-container">
71 + <?php
72 + $forum_query = new WP_Query( [
73 + 'post_type' => bbp_get_forum_post_type(),
74 + 'posts_per_page' => -1,
75 + 'post_parent' => 0,
76 + 'orderby' => 'menu_order',
77 + 'order' => 'ASC',
78 + 'post_status' => 'publish',
79 + ] );
80 + $count = $forum_query->found_posts;
81 + // Left Sidebar Forums.
82 + include __DIR__ . '/admin_ui/forums.php';
83 + ?>
84 + <div class="easydocs-tab-content">
85 + <?php
86 + $ids = 0;
87 + $container = 1;
88 + if ( is_array( $parent_forums ) ) :
89 + foreach ( $parent_forums as $item ) :
90 + $ids ++;
91 + $container ++;
92 + $active = $ids == 1 ? ' tab-active' : '';
93 +
94 + $children = new WP_Query(
95 + [
96 + 'post_parent' => $item,
97 + 'post_type' => bbp_get_topic_post_type(),
98 + 'orderby' => 'menu_order',
99 + 'order' => 'asc',
100 + 'posts_per_page' => -1,
101 + 'post_status' => [ 'any', 'spam' ],
102 + ]
103 + );
104 +
105 + $count_open = 0;
106 + $count_closed = 0;
107 + $count_hidden = 0;
108 + $count_no_reply = 0;
109 + $count_solved = 0;
110 + $count_unsolved = 0;
111 + $count_trash = 0;
112 +
113 + $reply_stats = forumax_admin_reply_stats( wp_list_pluck( $children->posts, 'ID' ) );
114 +
115 + while ( $children->have_posts() ) : $children->the_post();
116 + $topic_id = get_the_ID();
117 +
118 + // Count open/closed topics.
119 + if ( bbp_is_topic_closed( $topic_id ) ) {
120 + $count_closed++;
121 + } else {
122 + $count_open++;
123 + }
124 +
125 + // Count spam( hidden ) topics.
126 + if ( bbp_is_topic_spam( $topic_id ) || bbp_is_topic_pending( $topic_id ) ) {
127 + $count_hidden++;
128 + }
129 +
130 + // Replies count.
131 + if ( empty( $reply_stats['total'][ $topic_id ] ) ) {
132 + $count_no_reply++;
133 + }
134 +
135 + // Count solved.
136 + // Before using $GLOBALS['forumax_solve_topic'], check if it is set
137 + if (isset($GLOBALS['forumax_solve_topic']) && $GLOBALS['forumax_solve_topic']->is_solved($topic_id)) {
138 + $count_solved++;
139 + } else {
140 + $count_unsolved++;
141 + }
142 + endwhile;
143 + wp_reset_postdata();
144 +
145 + // Count trashed topics without loading them.
146 + $trash_topic = new WP_Query( [
147 + 'post_parent' => $item,
148 + 'post_type' => bbp_get_topic_post_type(),
149 + 'posts_per_page' => 1,
150 + 'post_status' => [ 'trash' ],
151 + 'fields' => 'ids',
152 + 'update_post_meta_cache' => false,
153 + 'update_post_term_cache' => false,
154 + ] );
155 + $count_trash = (int) $trash_topic->found_posts;
156 + ?>
157 + <div class="easydocs-tab <?php echo esc_attr( $active ); ?>" id="tab-<?php echo esc_attr( $item ); ?>">
158 +
159 + <!-- Tab filters. -->
160 + <?php include __DIR__ . '/admin_ui/tab_filters.php'; ?>
161 +
162 + <!-- Children topics. -->
163 + <?php include __DIR__ . '/admin_ui/topics.php'; ?>
164 +
165 + <?php if ( $children->post_count > 0 ) : ?>
166 + <div class="controls bbpc-admin-pagination" style="display:none;">
167 + <div class="mixitup-control mixitup-page-list"></div>
168 + </div>
169 + <?php endif; ?>
170 +
171 + <a class="easydocs-btn easydocs-btn-outline-blue easydocs-btn-sm easydocs-btn-round button button-info section-doc" id="bbpc-topic" target="_blank" name="submit" href="javascript:void(0)" bbp_forum_id="<?php echo esc_attr( $item ); ?>">
172 + <?php esc_html_e( 'Add Topic', 'forumax' ); ?>
173 + </a>
174 + </div>
175 + <?php
176 + endforeach;
177 + endif;
178 + ?>
179 + </div>
180 + </div>
181 + </div>
182 + </main>
183 + <?php
184 + else :
185 + ?>
186 + <div class="eazydocs-no-content">
187 + <img src="<?php echo esc_url( FORUMAX_IMG . 'icon/folder-open.png' ); ?>" alt="<?php esc_attr_e( 'Folder Open', 'forumax' ); ?>">
188 + <p class="big-p"> <?php esc_html_e( 'No forums found. Get started by creating a new forum or importing demo data.', 'forumax' ); ?> </p>
189 + <p class="forumax-empty-actions">
190 + <a href="javascript:void(0)" type="button" id="bbpc-forum" class="button button-primary ezd-btn btn-lg">
191 + <span class="dashicons dashicons-plus-alt2"></span>
192 + <?php esc_html_e( 'Create Forum', 'forumax' ); ?>
193 + </a>
194 + <span class="forumax-actions-separator"><?php esc_html_e( 'or', 'forumax' ); ?></span>
195 + <a href="javascript:void(0)" type="button" id="forumax-import-demo" class="button ezd-btn btn-lg forumax-import-demo-btn">
196 + <span class="dashicons dashicons-database-import"></span>
197 + <?php esc_html_e( 'Import Demo Data', 'forumax' ); ?>
198 + </a>
199 + </p>
200 + </div>
201 + <?php
202 + endif;
203 + ?>
204 + </div>
205 +</div>
206 +
207 +<script>
208 + (function ($) {
209 + $(document).ready(function () {
210 + let docContainer = document.querySelectorAll('.easydocs-tab');
211 + var config = {
212 + controls: {
213 + scope: 'local',
214 + },
215 + animation: {
216 + enable: false,
217 + },
218 + load: {
219 + filter: '<?php echo esc_js( forumax_get_opt( 'default_filter', '.open-topics' ) ); ?>'
220 + },
221 + pagination: {
222 + limit: 10,
223 + },
224 + callbacks: {
225 + onMixEnd: function(state) {
226 + var pagination = state.container.querySelector('.bbpc-admin-pagination');
227 + if ( pagination ) {
228 + // Count matching items directly from the DOM using the active
229 + // filter selector. MixItUp always renders prev/next buttons so
230 + // children.length is unreliable for detecting multi-page results.
231 + var selector = state.activeFilter ? state.activeFilter.selector : '';
232 + var matchCount = ( ! selector || selector === 'all' )
233 + ? state.container.querySelectorAll('.mix').length
234 + : state.container.querySelectorAll(selector).length;
235 + pagination.style.display = matchCount > 10 ? 'block' : 'none';
236 + }
237 + }
238 + }
239 + };
240 + for (let i = 0; i < docContainer.length; i++) {
241 + var mixer1 = mixitup(docContainer[i], config);
242 + }
243 + });
244 +})(jQuery);
172 245 </script>