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
bbp-core / includes / features / bbp_solved_topic.php

bbp_solved_topic.php in Forumax – AI Powered Advanced Community Forum Plugin 2.5.1, at includes/features/bbp_solved_topic.php

462 lines 12.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace features;
3
4 class forumax_solve_topic {
5
6 /**
7 * The capability required to view private posts.
8 *
9 * @since 1.0
10 *
11 * @var string $capability
12 */
13 public $capability = 'moderate';
14
15 /*
16 --------------------------------------------*
17 * Constructor
18 *--------------------------------------------*/
19
20 /**
21 * Initializes the plugin by setting filters, and administration functions.
22 */
23 function __construct() {
24 add_action( 'plugins_loaded', [ $this, 'filter_capability' ] );
25
26 // show the checkboxes
27 add_action( 'bbp_theme_before_reply_form_submit_wrapper', [ $this, 'reply_checkbox' ] );
28 add_action( 'bbp_theme_before_topic_form_submit_wrapper', [ $this, 'topic_checkbox' ] );
29
30 // save the reply state
31 add_action( 'bbp_new_reply', [ $this, 'update_reply' ], 0, 6 );
32 add_action( 'bbp_edit_reply', [ $this, 'update_reply' ], 0, 6 );
33
34 // save the topic state
35 add_action( 'bbp_new_topic', [ $this, 'update_topic' ], 0, 6 );
36 add_action( 'bbp_edit_topic', [ $this, 'update_topic' ], 0, 6 );
37
38 // reply content filter
39 add_filter( 'bbp_get_reply_excerpt', [ $this, 'reply_content_filter' ], 999, 2 );
40 add_filter( 'bbp_get_reply_content', [ $this, 'reply_content_filter' ], 999, 2 );
41
42 add_filter( 'bbp_theme_before_topic_title', [ $this, 'solved_badge' ], 999, 2 );
43 add_filter( 'bbp_topic_admin_links', [ $this, 'add_topic_admin_link' ], 10, 2 );
44
45 add_action( 'template_redirect', [ $this, 'handle_solve_action' ] );
46
47 // add a class name indicating the read status
48 add_filter( 'post_class', [ $this, 'reply_post_class' ] );
49 }
50
51 /**
52 * Handle the solve/unsolve action via GET request
53 *
54 * @since 2.2.0
55 */
56 public function handle_solve_action() {
57 if ( ! isset( $_GET['solve_topic'] ) && ! isset( $_GET['unsolve_topic'] ) ) {
58 return;
59 }
60
61 $topic_id = bbp_get_topic_id();
62 $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
63
64 if ( isset( $_GET['solve_topic'] ) ) {
65 // Verify nonce
66 if ( ! wp_verify_nonce( $nonce, 'forumax_solve_topic_' . $topic_id ) ) {
67 wp_die( esc_html__( 'Security check failed.', 'forumax' ) );
68 }
69 $this->solve_topic( $topic_id );
70 } elseif ( isset( $_GET['unsolve_topic'] ) ) {
71 if ( ! wp_verify_nonce( $nonce, 'forumax_unsolve_topic_' . $topic_id ) ) {
72 wp_die( esc_html__( 'Security check failed.', 'forumax' ) );
73 }
74 $this->unsolve_topic( $topic_id );
75 }
76 }
77
78 /**
79 * Admin links
80 */
81 public function add_topic_admin_link( $links, $topic_id ) {
82
83 $topic_author = bbp_get_topic_author_id( bbp_get_topic_id() );
84 if ( ( current_user_can( $this->capability ) ) || ( $topic_author == get_current_user_id() ) ) {
85 if ( $this->is_solved( bbp_get_topic_id() ) ) {
86 $links['unsolved'] = '<a href="' . wp_nonce_url(
87 add_query_arg( 'unsolve_topic', '1', bbp_get_topic_permalink() ),
88 'forumax_unsolve_topic_' . bbp_get_topic_id()
89 ) . '">' . esc_html__( 'Mark as unsolved', 'forumax' ) . '</a>';
90 } else {
91 $links['solved'] = '<a href="' . wp_nonce_url(
92 add_query_arg( 'solve_topic', '1', bbp_get_topic_permalink() ),
93 'forumax_solve_topic_' . bbp_get_topic_id()
94 ) . '">' . esc_html__( 'Mark as solved', 'forumax' ) . '</a>';
95 }
96 }
97 return $links;
98 }
99
100 public function solve_topic( $topic_id = 0 ) {
101 $topic_author = bbp_get_topic_author_id( $topic_id );
102 if ( ( current_user_can( $this->capability ) ) || ( $topic_author == get_current_user_id() ) ) {
103 update_post_meta( $topic_id, '_bbp_topic_is_solved', '1' );
104 $redirect = remove_query_arg( [ 'solve_topic', '_wpnonce' ] );
105 wp_safe_redirect( $redirect );
106 exit;
107 } else {
108 wp_die( esc_html__( 'You do not have the permission to do that!', 'forumax' ) );
109 }
110
111 }
112
113 public function unsolve_topic( $topic_id = 0 ) {
114 $topic_author = bbp_get_topic_author_id( $topic_id );
115 if ( ( current_user_can( $this->capability ) ) || ( $topic_author == get_current_user_id() ) ) {
116 delete_post_meta( $topic_id, '_bbp_topic_is_solved' );
117 $redirect = remove_query_arg( [ 'unsolve_topic', '_wpnonce' ] );
118 wp_safe_redirect( $redirect );
119 exit;
120 } else {
121 wp_die( esc_html__( 'You do not have the permission to do that!', 'forumax' ) );
122 }
123 }
124
125 /**
126 * Called during the plugins_loaded action to filter the capability
127 * required to view solved topics.
128 *
129 * @since 1.0
130 *
131 * @return void
132 */
133 public function filter_capability() {
134 $this->capability = apply_filters( 'forumax_solve_topic_capability', $this->capability );
135 }
136
137 /**
138 * Solved topic badge
139 *
140 * @since 1.0
141 *
142 * @return void
143 */
144 public function solved_badge() {
145 $topic_id = bbp_get_topic_id();
146 if ( $this->is_solved( $topic_id ) ) {
147 echo '<i class="fa fa-check-circle text-success"></i>';
148 }
149 }
150
151 /**
152 * Outputs the reply checkbox
153 *
154 * @since 1.0
155 *
156 * @return void
157 */
158 public function reply_checkbox() {
159 $has_best_answer = $this->has_best_answer( bbp_get_topic_id() );
160 $is_best_answer = $this->is_best_answer( bbp_get_reply_id() );
161 if ( bbp_is_reply_edit() && ( current_user_can( $this->capability ) ) ) {
162 if ( ( ! $has_best_answer ) || ( $is_best_answer ) ) {
163 ?>
164 <p>
165 <input name="bbp_best_answer" id="bbp_best_answer" type="checkbox"<?php checked( '1', $is_best_answer ); ?> value="1" tabindex="<?php bbp_tab_index(); ?>" />
166 <label for="bbp_best_answer"><?php esc_html_e( 'Set this reply as Best Answer.', 'forumax' ); ?></label>
167 </p>
168 <?php
169 }
170 }
171 }
172
173 /**
174 * Outputs the topic checkbox
175 *
176 * @since 1.0
177 *
178 * @return void
179 */
180 public function topic_checkbox() {
181 $topic_author = bbp_get_topic_author_id( bbp_get_topic_id() );
182 if ( bbp_is_topic_edit() && ( ( current_user_can( $this->capability ) ) || ( $topic_author == get_current_user_id() ) ) ) {
183 ?>
184 <p>
185 <input name="forumax_solve_topic" id="forumax_solve_topic" type="checkbox"<?php checked( '1', $this->is_solved( bbp_get_topic_id() ) ); ?> value="1" tabindex="<?php bbp_tab_index(); ?>" />
186 <label for="forumax_solve_topic"><?php esc_html_e( 'Set this post as solved.', 'forumax' ); ?></label>
187 </p>
188 <?php
189 }
190
191 }
192
193 /**
194 * Stores the private state on reply creation and edit
195 *
196 * @since 1.0
197 *
198 * @param $reply_id int The ID of the reply
199 * @param $topic_id int The ID of the topic the reply belongs to
200 * @param $forum_id int The ID of the forum the topic belongs to
201 * @param $anonymous_data bool Are we posting as an anonymous user?
202 * @param $author_id int The ID of user creating the reply, or the ID of the replie's author during edit
203 * @param $is_edit bool Are we editing a reply?
204 *
205 * @return void
206 */
207 public function update_reply( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = false, $author_id = 0, $is_edit = false ) {
208
209 if ( isset( $_POST['bbp_best_answer'] ) ) {
210 $get_topic_meta = get_post_meta( $topic_id, '_bbp_topic_has_best_answer', true );
211 if ( ! $get_topic_meta ) {
212 update_post_meta( $topic_id, '_bbp_topic_has_best_answer', '1' );
213 }
214 update_post_meta( $reply_id, '_bbp_reply_is_best_answer', '1' );
215 } elseif ( get_post_meta( $reply_id, '_bbp_reply_is_best_answer', true ) ) {
216 // Only clear the topic flag when this reply was the best answer;
217 // saving any other reply must not wipe it.
218 delete_post_meta( $reply_id, '_bbp_reply_is_best_answer' );
219 delete_post_meta( $topic_id, '_bbp_topic_has_best_answer' );
220 }
221
222 }
223
224 /**
225 * Stores the private state on topic creation and edit
226 *
227 * @since 1.0
228 *
229 * @param $topic_id int The ID of the topic
230 * @param $is_edit bool Are we editing a topic?
231 *
232 * @return void
233 */
234 public function update_topic( $topic_id = 0, $is_edit = false ) {
235
236 if ( isset( $_POST['forumax_solve_topic'] ) ) {
237 update_post_meta( $topic_id, '_bbp_topic_is_solved', '1' );
238 } else {
239 delete_post_meta( $topic_id, '_bbp_topic_is_solved' );
240 }
241
242 }
243
244
245 /**
246 * Determines if a reply is marked as solved
247 *
248 * @since 1.0
249 *
250 * @param $reply_id int The ID of the reply
251 *
252 * @return bool
253 */
254 public function is_best_answer( $reply_id = 0 ) {
255
256 $retval = false;
257
258 // Checking a specific reply id
259 if ( ! empty( $reply_id ) ) {
260 $reply = bbp_get_reply( $reply_id );
261 $reply_id = ! empty( $reply ) ? $reply->ID : 0;
262
263 // Using the global reply id
264 } elseif ( bbp_get_reply_id() ) {
265 $reply_id = bbp_get_reply_id();
266
267 // Use the current post id
268 } elseif ( ! bbp_get_reply_id() ) {
269 $reply_id = get_the_ID();
270 }
271
272 if ( ! empty( $reply_id ) ) {
273 $retval = get_post_meta( $reply_id, '_bbp_reply_is_best_answer', true );
274 }
275
276 return (bool) apply_filters( 'bbp_reply_is_best_answer', (bool) $retval, $reply_id );
277 }
278
279 /**
280 * Determines if a reply is marked as solved
281 *
282 * @since 1.0
283 *
284 * @param $reply_id int The ID of the reply
285 *
286 * @return bool
287 */
288 public function is_solved( $topic_id = 0 ) {
289
290 $retval = false;
291
292 // Checking a specific reply id.
293 if ( ! empty( $topic_id ) ) {
294 $topic = bbp_get_topic( $topic_id );
295 $topic_id = ! empty( $topic ) ? $topic->ID : 0;
296
297 // Using the global reply id.
298 } elseif ( bbp_get_topic_id() ) {
299 $topic_id = bbp_get_topic_id();
300
301 // Use the current post id.
302 } elseif ( ! bbp_get_topic_id() ) {
303 $topic_id = get_the_ID();
304 }
305
306 if ( ! empty( $topic_id ) ) {
307 $retval = get_post_meta( $topic_id, '_bbp_topic_is_solved', true );
308 }
309
310 return (bool) apply_filters( 'bbp_topic_is_solved', (bool) $retval, $topic_id );
311 }
312
313 /**
314 * Determines if a reply is marked as solved
315 *
316 * @since 1.0
317 *
318 * @param $reply_id int The ID of the reply
319 *
320 * @return bool
321 */
322 public function has_best_answer( $topic_id = 0 ) {
323
324 // if bbpress is not active, return the classes
325
326 if ( ! function_exists( 'bbp_get_topic_id' ) ) {
327 return false;
328 }
329
330 $retval = false;
331
332 // Checking a specific reply id.
333 if ( ! empty( $topic_id ) ) {
334 $topic = bbp_get_topic( $topic_id );
335 $topic_id = ! empty( $topic ) ? $topic->ID : 0;
336
337 // Using the global reply id.
338 } elseif ( bbp_get_topic_id() ) {
339 $topic_id = bbp_get_topic_id();
340
341 // Use the current post id.
342 } elseif ( ! bbp_get_topic_id() ) {
343 $topic_id = get_the_ID();
344 }
345
346 if ( ! empty( $topic_id ) ) {
347 $retval = get_post_meta( $topic_id, '_bbp_topic_has_best_answer', true );
348 }
349
350 return (bool) apply_filters( 'bbp_topic_has_best_answer', (bool) $retval, $topic_id );
351 }
352
353
354 /**
355 * Reply content filter
356 *
357 * @since 1.0
358 *
359 * @param $content string The content of the reply
360 * @param $reply_id int The ID of the reply
361 *
362 * @return string
363 */
364 public function reply_content_filter( $content = '', $reply_id = 0 ) {
365
366 if ( empty( $reply_id ) ) {
367 $reply_id = bbp_get_reply_id( $reply_id );
368 }
369
370 $topic_id = bbp_get_topic_id();
371
372 if ( $this->is_best_answer( $reply_id ) ) {
373 $content = '<div class="solved-topic-bar" id="best-answer"><span class="badge"><i class="icon_check"></i> ' . esc_html__( 'Best Answer', 'forumax' ) . '</span></div>' . $content;
374 }
375
376 return $content;
377 }
378
379 /**
380 * Topic content filter
381 *
382 * @since 1.0
383 *
384 * @param $content string The content of the topic
385 * @param $topic_id int The ID of the reply
386 *
387 * @return string
388 */
389 public function topic_content_filter( $content = '', $topic_id = 0 ) {
390
391 if ( empty( $topic_id ) ) {
392 $topic_id = bbp_get_topic_id( $topic_id );
393 }
394
395 $open = '';
396 $close = '';
397 $resolved = '';
398 $has_best_answer = '';
399
400 if ( $this->is_solved( $topic_id ) || $this->has_best_answer( $topic_id ) ) {
401 $open = '<div class="solved-topic-bar">';
402 $close = '</div>';
403 }
404
405 if ( $this->is_solved( $topic_id ) ) {
406 $resolved = '<span class="accepted-ans-mark badge"><i class="icon_check_alt"></i> ' . esc_html__( 'Resolved', 'forumax' ) . '</span>';
407 }
408
409 $content = $content . $open . $resolved . $has_best_answer . $close;
410
411 return $content;
412 }
413
414 /**
415 * Adds a new class to replies that are marked as solved
416 *
417 * @since 1.0
418 *
419 * @param $classes array An array of current class names
420 *
421 * @return bool
422 */
423 public function reply_post_class( $classes ) {
424
425 // if bbpress is not active, return the classes
426 if ( ! function_exists( 'bbp_get_reply_id' ) ) {
427 return $classes;
428 }
429
430 $reply_id = bbp_get_reply_id();
431
432 // Only apply the class to replies.
433 if ( bbp_get_reply_post_type() != get_post_type( $reply_id ) ) {
434 return $classes;
435 }
436
437 if ( $this->is_best_answer( $reply_id ) ) {
438 $classes[] = 'best-answer';
439 }
440
441 return $classes;
442 }
443 }
444
445 // Instantiate the class.
446 $GLOBALS['forumax_solve_topic'] = new forumax_solve_topic();
447
448 /**
449 * Shared solved-topic instance.
450 *
451 * Constructing a new instance re-registers every hook, so repeated calls
452 * (once per topic in a loop) duplicated badges and filters.
453 *
454 * @return forumax_solve_topic
455 */
456 function forumax_solve_topic(): forumax_solve_topic {
457 if ( ! ( $GLOBALS['forumax_solve_topic'] ?? null ) instanceof forumax_solve_topic ) {
458 $GLOBALS['forumax_solve_topic'] = new forumax_solve_topic();
459 }
460
461 return $GLOBALS['forumax_solve_topic'];
462 }