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/features/bbpc_attachments/code/public.php +153 -19 1.2.9 → 2.5.1 View file →
@@ -9,40 +9,171 @@
9 9 * @param int $post_id topic or reply ID to get attachments for
10 10 *
11 11 * @return array list of attachments objects
12 12 */
13 -function bbpc_get_post_attachments( $post_id ) {
14 - $args = [
15 - 'post_type' => 'attachment',
16 - 'numberposts' => -1,
17 - 'post_status' => null,
18 - 'post_parent' => $post_id,
19 - 'orderby' => 'ID',
20 - 'order' => 'ASC',
21 - ];
13 +function forumax_get_post_attachments( $post_id ) {
14 + $post_id = (int) $post_id;
15 + $store = &forumax_post_attachments_store();
22 16
23 - return get_posts( $args );
17 + if ( ! isset( $store[ $post_id ] ) ) {
18 + // Prime the whole topic/reply loop in one query instead of one per post.
19 + forumax_prime_post_attachments( array_merge( [ $post_id ], forumax_current_loop_post_ids() ) );
20 + }
21 +
22 + return $store[ $post_id ] ?? [];
24 23 }
25 24
26 25 /**
26 + * Per-request attachment store, keyed by parent post ID.
27 + *
28 + * @return array
29 + */
30 +function &forumax_post_attachments_store() {
31 + static $store = [];
32 + return $store;
33 +}
34 +
35 +/**
36 + * IDs of the topics/replies in the active bbPress loops.
37 + *
38 + * @return int[]
39 + */
40 +function forumax_current_loop_post_ids() {
41 + $ids = [];
42 +
43 + if ( ! function_exists( 'bbpress' ) ) {
44 + return $ids;
45 + }
46 +
47 + foreach ( [ 'reply_query', 'topic_query' ] as $prop ) {
48 + $query = bbpress()->{$prop} ?? null;
49 + if ( $query instanceof WP_Query && ! empty( $query->posts ) ) {
50 + $ids = array_merge( $ids, forumax_query_post_ids( $query ) );
51 + }
52 + }
53 +
54 + return $ids;
55 +}
56 +
57 +/**
58 + * Post IDs of a query, whether it fetched objects or IDs.
59 + *
60 + * @param WP_Query $query Query.
61 + * @return int[]
62 + */
63 +function forumax_query_post_ids( $query ) {
64 + return array_map(
65 + static function ( $post ) {
66 + return is_object( $post ) ? (int) $post->ID : (int) $post;
67 + },
68 + (array) $query->posts
69 + );
70 +}
71 +
72 +/**
73 + * Fetch attachments for many parent posts in a single query.
74 + *
75 + * @param int[] $post_ids Topic or reply IDs.
76 + * @return void
77 + */
78 +function forumax_prime_post_attachments( array $post_ids ) {
79 + $store = &forumax_post_attachments_store();
80 + $post_ids = array_values( array_diff( array_unique( array_filter( array_map( 'intval', $post_ids ) ) ), array_keys( $store ) ) );
81 +
82 + if ( empty( $post_ids ) ) {
83 + return;
84 + }
85 +
86 + foreach ( $post_ids as $id ) {
87 + $store[ $id ] = [];
88 + }
89 +
90 + // No row cap: the parent set is already bounded by the current loop, and a
91 + // cap would silently drop attachments of the posts past it (each of them
92 + // is marked as loaded above).
93 + $attachments = get_posts(
94 + [
95 + 'post_type' => 'attachment',
96 + 'numberposts' => -1,
97 + 'post_status' => 'inherit',
98 + 'post_parent__in' => $post_ids,
99 + 'orderby' => 'ID',
100 + 'order' => 'ASC',
101 + 'update_post_term_cache' => false,
102 + ]
103 + );
104 +
105 + foreach ( $attachments as $attachment ) {
106 + $store[ (int) $attachment->post_parent ][] = $attachment;
107 + }
108 +}
109 +
110 +/**
111 + * Forget a parent's cached attachments when one is added or removed.
112 + *
113 + * @param int $attachment_id Attachment ID.
114 + * @return void
115 + */
116 +function forumax_flush_post_attachments_store( $attachment_id ) {
117 + $store = &forumax_post_attachments_store();
118 + $parent = (int) wp_get_post_parent_id( $attachment_id );
119 + unset( $store[ $parent ] );
120 +}
121 +add_action( 'add_attachment', 'forumax_flush_post_attachments_store' );
122 +add_action( 'delete_attachment', 'forumax_flush_post_attachments_store' );
123 +
124 +/**
27 125 * Count attachments for the forum topic. It can include topic replies in the count.
28 126 *
127 + * Counts for every topic in the current loop are fetched together, so the
128 + * topic list costs two queries instead of one per topic.
129 + *
29 130 * @param int $topic_id id of the topic to count attachments for
30 131 * @param bool $include_replies true, to include reply attachments
31 132 *
32 133 * @return int number of attachments
33 134 */
34 -function bbpc_topic_attachments_count( $topic_id, $include_replies = false ) {
135 +function forumax_topic_attachments_count( $topic_id, $include_replies = false ) {
35 136 global $wpdb;
137 + static $counts = [];
36 138
37 - $sql = 'select ID from ' . $wpdb->posts . ' where post_parent = ' . $topic_id . " and post_type = 'attachment'";
139 + $topic_id = (int) $topic_id;
140 + $key = $include_replies ? 'all' : 'own';
38 141
142 + if ( $topic_id <= 0 ) {
143 + return 0;
144 + }
145 +
146 + if ( isset( $counts[ $key ][ $topic_id ] ) ) {
147 + return $counts[ $key ][ $topic_id ];
148 + }
149 +
150 + $topic_ids = [ $topic_id ];
151 + if ( function_exists( 'bbpress' ) && bbpress()->topic_query instanceof WP_Query ) {
152 + $topic_ids = array_merge( $topic_ids, forumax_query_post_ids( bbpress()->topic_query ) );
153 + }
154 + $topic_ids = array_values( array_unique( array_filter( $topic_ids ) ) );
155 +
156 + foreach ( $topic_ids as $id ) {
157 + $counts[ $key ][ $id ] = 0;
158 + }
159 +
160 + $placeholders = implode( ',', array_fill( 0, count( $topic_ids ), '%d' ) );
161 +
162 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
163 + $rows = $wpdb->get_results( $wpdb->prepare( "SELECT post_parent AS topic_id, COUNT(*) AS total FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_parent IN ($placeholders) GROUP BY post_parent", $topic_ids ) );
164 +
39 165 if ( $include_replies ) {
40 - $sql = '(' . $sql . ') union (select ID from ' . $wpdb->posts . ' where post_parent in (select ID from ' . $wpdb->posts . ' where post_parent = ' . $topic_id . " and post_type = 'reply') and post_type = 'attachment')";
166 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
167 + $reply_rows = $wpdb->get_results( $wpdb->prepare( "SELECT r.post_parent AS topic_id, COUNT(*) AS total FROM {$wpdb->posts} r INNER JOIN {$wpdb->posts} a ON a.post_parent = r.ID AND a.post_type = 'attachment' WHERE r.post_type = 'reply' AND r.post_parent IN ($placeholders) GROUP BY r.post_parent", $topic_ids ) );
168 + $rows = array_merge( $rows, $reply_rows );
41 169 }
42 170
43 - $attachments = $wpdb->get_results( $sql );
44 - return count( $attachments );
171 + foreach ( $rows as $row ) {
172 + $counts[ $key ][ (int) $row->topic_id ] += (int) $row->total;
173 + }
174 +
175 + return $counts[ $key ][ $topic_id ];
45 176 }
46 177
47 178 /**
48 179 * Handle upload file error.
@@ -51,9 +182,9 @@
51 182 * @param string $message error message
52 183 *
53 184 * @return WP_Error error message
54 185 */
55 -function bbpc_bbattachment_handle_upload_error( &$file, $message ) {
186 +function forumax_bbattachment_handle_upload_error( &$file, $message ) {
56 187 return new WP_Error( 'wp_upload_error', $message );
57 188 }
58 189
59 190 /**
@@ -60,9 +191,9 @@
60 191 * Get current page forum ID. Handles the edge cases with the edit forms.
61 192 *
62 193 * @return int
63 194 */
64 -function bbpc_get_forum_id() {
195 +function forumax_get_forum_id() {
65 196 $forum_id = bbp_get_forum_id();
66 197
67 198 if ( $forum_id == 0 ) {
68 199 if ( bbp_is_topic_edit() ) {
@@ -76,7 +207,10 @@
76 207
77 208 return $forum_id;
78 209 }
79 210
80 -function bbpc_bba_o( $name ) {
81 - return BBPCATTCore::instance()->o[ $name ];
211 +function forumax_bba_o( $name ) {
212 + $opts = BBPCATTCore::instance()->o;
213 + // Return the option value if present, otherwise null.
214 + return isset( $opts[ $name ] ) ? $opts[ $name ] : null;
82 215 }
216 +