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 +141 -82 1.1.0 → 2.5.1 View file →
@@ -1,13 +1,64 @@
1 1 <?php
2 2 if ( ! class_exists( 'bbPress' ) ) {
3 3 return;
4 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 +
5 58 $parent_forums = [];
6 59 $fcount = wp_count_posts( bbp_get_forum_post_type() );
7 60 $forum_count = (int) ( $fcount->publish + $fcount->hidden + $fcount->spam );
8 -$bbpc_opt = get_option( 'bbp_core_settings' );
9 -$filter_set = $bbpc_opt['filter_buttons'] ?? [ 'open', 'closed', 'hidden', 'no_reply', 'all', 'trash' ];
10 61 ?>
11 62 <div class="wrap">
12 63 <div class="body-dark">
13 64 <?php
@@ -13,30 +64,24 @@
13 64 <?php
14 65 if ( $forum_count > 0 ) :
15 66 include __DIR__ . '/admin_ui/header.php';
16 67 ?>
17 -
18 68 <main>
19 69 <div class="easydocs-sidebar-menu">
20 70 <div class="tab-container">
21 71 <?php
22 - $forum_query = new WP_Query(
23 - [
24 - 'post_type' => bbp_get_forum_post_type(),
25 - 'posts_per_page' => -1,
26 - 'post_parent' => 0,
27 - 'orderby' => 'menu_order',
28 - 'order' => 'ASC',
29 - 'post_status' => 'publish',
30 - ]
31 - );
32 -
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 + ] );
33 80 $count = $forum_query->found_posts;
34 -
35 81 // Left Sidebar Forums.
36 82 include __DIR__ . '/admin_ui/forums.php';
37 83 ?>
38 -
39 84 <div class="easydocs-tab-content">
40 85 <?php
41 86 $ids = 0;
42 87 $container = 1;
@@ -64,20 +109,13 @@
64 109 $count_solved = 0;
65 110 $count_unsolved = 0;
66 111 $count_trash = 0;
67 112
68 - while ( $children->have_posts() ) :
69 - $children->the_post();
113 + $reply_stats = forumax_admin_reply_stats( wp_list_pluck( $children->posts, 'ID' ) );
114 +
115 + while ( $children->have_posts() ) : $children->the_post();
70 116 $topic_id = get_the_ID();
71 117
72 - $replies = get_children(
73 - [
74 - 'post_parent' => $topic_id,
75 - 'post_type' => bbp_get_reply_post_type(),
76 - 'post_status' => [ 'publish', 'draft', 'pending' ],
77 - ]
78 - );
79 -
80 118 // Count open/closed topics.
81 119 if ( bbp_is_topic_closed( $topic_id ) ) {
82 120 $count_closed++;
83 121 } else {
@@ -84,88 +122,93 @@
84 122 $count_open++;
85 123 }
86 124
87 125 // Count spam( hidden ) topics.
88 - if ( bbp_is_topic_spam( $topic_id ) ) {
126 + if ( bbp_is_topic_spam( $topic_id ) || bbp_is_topic_pending( $topic_id ) ) {
89 127 $count_hidden++;
90 128 }
91 129
92 130 // Replies count.
93 - if ( 0 == count( $replies ) ) {
131 + if ( empty( $reply_stats['total'][ $topic_id ] ) ) {
94 132 $count_no_reply++;
95 133 }
96 134
97 135 // Count solved.
98 - if ( $GLOBALS['bbp_solved_topic']->is_solved( $topic_id ) ) {
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)) {
99 138 $count_solved++;
100 139 } else {
101 140 $count_unsolved++;
102 141 }
103 -
142 + endwhile;
143 + wp_reset_postdata();
104 144
105 - endwhile;
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 ); ?>">
106 158
107 - $trash_topic = new WP_Query(
108 - [
109 - 'post_parent' => $item,
110 - 'post_type' => bbp_get_topic_post_type(),
111 - 'posts_per_page' => -1,
112 - 'post_status' => [ 'trash' ],
113 - ]
114 - );
115 -
116 - while ( $trash_topic->have_posts() ) :
117 - $trash_topic->the_post();
118 - $trash_topic_id = get_the_ID();
119 - // Count trash.
120 - if ( bbp_is_topic_trash( $trash_topic_id ) ) {
121 - $count_trash++;
122 - }
123 - endwhile; wp_reset_postdata();
124 - ?>
125 - <div class="easydocs-tab <?php echo esc_attr( $active ); ?>" id="tab-<?php echo esc_attr( $item ); ?>">
159 + <!-- Tab filters. -->
160 + <?php include __DIR__ . '/admin_ui/tab_filters.php'; ?>
126 161
127 - <!-- Tab filters. -->
128 - <?php include __DIR__ . '/admin_ui/tab_filters.php'; ?>
162 + <!-- Children topics. -->
163 + <?php include __DIR__ . '/admin_ui/topics.php'; ?>
129 164
130 - <!-- Children topics. -->
131 - <?php include __DIR__ . '/admin_ui/topics.php'; ?>
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; ?>
132 170
133 - <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="<?php echo admin_url( 'admin.php' ); ?>/Create_Topic.php?bbp_parentID=<?php echo $item; ?>&is_bbp_section=">
134 - <?php esc_html_e( 'Add Topic', 'bbp-core' ); ?>
135 - </a>
136 - </div>
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>
137 175 <?php
138 - endforeach;
139 - endif;
140 - ?>
176 + endforeach;
177 + endif;
178 + ?>
179 + </div>
141 180 </div>
142 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>
143 200 </div>
144 - </main>
145 - <?php
146 - else :
201 + <?php
202 + endif;
147 203 ?>
148 - <div class="eazydocs-no-content">
149 - <img src="<?php echo BBPC_IMG; ?>/icon/folder-open.png" alt="<?php esc_attr_e( 'Folder Open', 'bbp-core' ); ?>">
150 - <p class="big-p"> <?php esc_html_e( 'No forum has been found . Perhaps', 'bbp-core' ); ?> </p>
151 - <p> <br>
152 - <a href="<?php echo admin_url( 'admin.php' ); ?>/Create_Forum.php?bbp_parent_title=" target="_blank" type="button" id="bbpc-forum" class="button button-primary ezd-btn btn-lg">
153 - <?php esc_html_e( 'Create Forum', 'bbp-core' ); ?>
154 - </a>
155 - </p>
156 - </div>
157 - <?php
158 - endif; //TODO: Fix open topics not being selected issue.
159 - ?>
204 + </div>
160 205 </div>
161 -</div>
162 206
163 207 <script>
164 208 (function ($) {
165 209 $(document).ready(function () {
166 210 let docContainer = document.querySelectorAll('.easydocs-tab');
167 -
168 211 var config = {
169 212 controls: {
170 213 scope: 'local',
171 214 },
@@ -172,15 +215,31 @@
172 215 animation: {
173 216 enable: false,
174 217 },
175 218 load: {
176 - filter: '<?php echo esc_js( $bbpc_opt['default_filter'] ?? '.open-topics' ); ?>'
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 + }
177 238 }
178 239 };
179 -
180 240 for (let i = 0; i < docContainer.length; i++) {
181 241 var mixer1 = mixitup(docContainer[i], config);
182 242 }
183 243 });
184 244 })(jQuery);
185 -</script>
186 -
245 +</script>