PluginProbe
wpForo Forum / 3.0.9
wpForo Forum v3.0.9
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 / widgets / RecentTopics.php

RecentTopics.php in wpForo Forum 3.0.9, at widgets/RecentTopics.php

333 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\widgets;
4
5 use WP_Widget;
6
7 class RecentTopics extends WP_Widget {
8 private $default_instance = [];
9 private $orderby_fields = [];
10 private $order_fields = [];
11
12 function __construct() {
13 parent::__construct( 'wpforo_recent_topics', 'wpForo Recent Topics', [ 'description' => 'Your forum\'s recent topics.' ] );
14 $this->init_local_vars();
15 add_action( 'wp_ajax_wpforo_load_ajax_widget_RecentTopics', [ $this, 'load_ajax_widget' ] );
16 add_action( 'wp_ajax_nopriv_wpforo_load_ajax_widget_RecentTopics', [ $this, 'load_ajax_widget' ] );
17 if( is_admin() ) {
18 add_action( 'wp_ajax_wpforo_get_forum_tree', [ $this, 'get_forum_tree' ] );
19 }
20 }
21
22 private function init_local_vars() {
23 $this->default_instance = [
24 'boardid' => 0,
25 'title' => 'Recent Topics',
26 'forumids' => [],
27 'orderby' => 'created',
28 'order' => 'DESC',
29 'count' => 9,
30 'display_avatar' => true,
31 'forumids_filter' => false,
32 'current_forumid_filter' => false,
33 'goto_unread' => false,
34 'refresh_interval' => 0,
35 ];
36 $this->orderby_fields = [
37 'created' => __( 'Created Date', 'wpforo' ),
38 'modified' => __( 'Modified Date', 'wpforo' ),
39 'posts' => __( 'Posts Count', 'wpforo' ),
40 'views' => __( 'Views Count', 'wpforo' ),
41 ];
42 $this->order_fields = [
43 'DESC' => __( 'DESC', 'wpforo' ),
44 'ASC' => __( 'ASC', 'wpforo' ),
45 'RAND' => __( 'Random', 'wpforo' ),
46 ];
47 }
48
49 private function add_topic_cache_filter( $topic_args ) {
50 if( $topic_args['order'] === 'RAND' ) {
51 add_filter( 'wpforo_cache_topic', '__return_false' );
52 }
53 }
54
55 private function remove_topic_cache_filter( $topic_args ) {
56 if( $topic_args['order'] === 'RAND' ) {
57 remove_filter( 'wpforo_cache_topic', '__return_false' );
58 }
59 }
60
61 public function get_widget( $instance, $topic_args ) {
62 $this->add_topic_cache_filter( $topic_args );
63 $is_user_logged_in = (bool) WPF()->current_userid;
64 $topic_args['private'] = ( ! $is_user_logged_in || ! WPF()->usergroup->can( 'aum' ) ) ? 0 : null;;
65 $topic_args['status'] = ( ! $is_user_logged_in || ! WPF()->usergroup->can( 'aum' ) ) ? 0 : null;
66
67 $row_count = (int) wpfval( $topic_args, 'row_count' );
68 $topics = [];
69 $topic_args['offset'] = 0;
70 while( $row_count && count( $topics ) < $row_count ) {
71 if( ! ( $_topics = WPF()->topic->get_topics( $topic_args ) ) ) break;
72
73 $topics = array_merge( $topics, $_topics );
74 $topic_args['offset'] += $row_count;
75 }
76 array_splice( $topics, $row_count );
77
78 $print_avatar = $instance['display_avatar'] && wpforo_setting( 'profiles', 'avatars' ) && WPF()->usergroup->can( 'va' );
79
80 $lis = '';
81 foreach( $topics as $topic ) {
82 $topic_url = wpforo_topic( $topic['topicid'], 'url' );
83 $member = wpforo_member( $topic );
84
85 $lis .= sprintf(
86 '<li>
87 <div class="wpforo-list-item">
88 %1$s
89 <div class="wpforo-list-item-right" %2$s>
90 <p class="posttitle">%3$s</p>
91 <p class="postuser">
92 %4$s %5$s <span style="white-space: nowrap;">%6$s</span>
93 </p>
94 </div>
95 <div class="wpf-clear"></div>
96 </div>
97 </li>',
98 ( $print_avatar ? sprintf( '<div class="wpforo-list-item-left">%1$s</div>', wpforo_user_avatar( $member ) ) : '' ),
99 ( ! $print_avatar ? 'style="width: 100%"' : '' ),
100 ( wpfval( $instance, 'goto_unread' ) ?
101 wpforo_topic_title( $topic, $topic_url, '{p}{au}{t}{/a}', false ) .
102 ( $topic['topicid'] != wpfval( WPF()->current_object, 'topicid' ) ? wpforo_unread_button( $topic['topicid'], $topic_url, false ) : '' )
103 :
104 wpforo_topic_title( $topic, $topic_url, '{p}{a}{t}{/a}', false )
105 ),
106 wpforo_phrase( 'by', false ),
107 wpforo_member_link( $member, '', 30, '', false ),
108 esc_html( wpforo_date( $topic['created'], 'ago', false ) )
109 );
110 }
111
112 $this->remove_topic_cache_filter( $topic_args );
113
114 return sprintf( '<ul>%1$s</ul>', $lis );
115 }
116
117 public function load_ajax_widget() {
118 $_POST = wp_unslash( $_POST );
119 $instance = json_decode( (string) wpfval( $_POST, 'instance' ), true );
120 $topic_args = json_decode( (string) wpfval( $_POST, 'topic_args' ), true );
121
122 // SECURITY FIX: Sanitize and validate all user-controlled parameters
123 if( is_array( $topic_args ) ) {
124 // Remove dangerous 'where' parameter
125 unset( $topic_args['where'] );
126
127 // Validate 'orderby' parameter against whitelist
128 if( isset( $topic_args['orderby'] ) ) {
129 if( ! key_exists( $topic_args['orderby'], $this->orderby_fields ) ) {
130 $topic_args['orderby'] = $this->default_instance['orderby'];
131 }
132 }
133
134 // Validate 'order' parameter against whitelist
135 if( isset( $topic_args['order'] ) ) {
136 if( ! key_exists( $topic_args['order'], $this->order_fields ) ) {
137 $topic_args['order'] = $this->default_instance['order'];
138 }
139 }
140 }
141
142 wp_send_json_success( [ 'html' => $this->get_widget( $instance, $topic_args ) ] );
143 }
144
145 public function widget( $args, $instance ) {
146 wp_enqueue_script( 'wpforo-widgets-js' );
147 $instance = wpforo_parse_args( $instance, $this->default_instance );
148 if( $instance['current_forumid_filter'] && $instance['boardid'] === WPF()->board->get_current( 'boardid' ) && $current_forumid = wpfval(
149 WPF()->current_object,
150 'forumid'
151 ) ) {
152 $instance['forumids'] = (array) $current_forumid;
153 }
154 $data = [
155 'boardid' => $instance['boardid'],
156 'action' => 'wpforo_load_ajax_widget_RecentTopics',
157 'instance' => $instance,
158 'topic_args' => [
159 'forumids' => ( $instance['forumids'] ?: $this->default_instance['forumids'] ),
160 'orderby' => ( key_exists( $instance['orderby'], $this->orderby_fields ) ? $instance['orderby'] : $this->default_instance['orderby'] ),
161 'order' => ( key_exists( $instance['order'], $this->order_fields ) ? $instance['order'] : $this->default_instance['order'] ),
162 'row_count' => ( ( $count = intval( $instance['count'] ) ) ? $count : $this->default_instance['count'] ),
163 ],
164 ];
165 if( WPF()->board->get_current( 'boardid' ) === $instance['boardid'] ) {
166 $html = $this->get_widget( $data['instance'], $data['topic_args'] );
167 $onload = false;
168 } else {
169 $html = '<div style="text-align: center; font-size: 20px;"><i class="fas fa-spinner fa-spin"></i></div>';
170 $onload = true;
171 $data['referer'] = home_url();
172 }
173 $json = wp_json_encode( $data );
174 echo $args['before_widget'] . '<div id="wpf-widget-recent-replies" class="wpforo-widget-wrap">';
175 if( ! empty( $instance['title'] ) ) echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];
176 echo '<div class="wpforo-widget-content wpforo-ajax-widget ' . ( ! $onload ? 'wpforo-ajax-widget-onload-false' : '' ) . '" data-json="' . esc_attr(
177 $json
178 ) . '">' . $html . '</div></div>' . $args['after_widget'];
179 }
180
181 public function form( $instance ) {
182 $instance = wpforo_parse_args( $instance, $this->default_instance );
183 $title = (string) $instance['title'];
184 $boardid = (int) $instance['boardid'];
185 $selected = array_unique( array_filter( array_map( 'intval', (array) $instance['forumids'] ) ) );
186 $orderby = (string) $instance['orderby'];
187 $order = (string) $instance['order'];
188 $count = (int) $instance['count'];
189 $display_avatar = (bool) $instance['display_avatar'];
190 $forumids_filter = (bool) $instance['forumids_filter'];
191 $current_forumid_filter = (bool) $instance['current_forumid_filter'];
192 $goto_unread = (bool) $instance['goto_unread'];
193 $refresh_interval = (int) $instance['refresh_interval'];
194 WPF()->change_board( $boardid );
195 ?>
196 <style>
197 .wpf-wdg-wrapper .wpf_wdg_forumids_wrap {
198 display: none !important;
199 }
200
201 .wpf-wdg-wrapper input.wpf_wdg_forumids_filter_1:checked ~ .wpf_wdg_forumids_wrap {
202 display: block !important;
203 }
204 </style>
205 <div class="wpf-wdg-wrapper">
206 <p>
207 <label for="<?php echo $this->get_field_id( 'title' ) ?>"><?php _e( 'Title', 'wpforo' ); ?>:</label>
208 <input id="<?php echo $this->get_field_id( 'title' ) ?>" class="widefat"
209 name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text"
210 value="<?php echo esc_attr( $title ); ?>">
211 </p>
212 <p>
213 <label for="<?php echo $this->get_field_id( 'boardid' ) ?>"></label>
214 <select id="<?php echo $this->get_field_id( 'boardid' ) ?>" class="wpf_wdg_boardid" name="<?php echo esc_attr( $this->get_field_name( 'boardid' ) ); ?>">
215 <?php echo WPF()->board->dropdown( $boardid ) ?>
216 </select>
217 </p>
218 <div>
219 <span><?php _e( 'Filter by forums', 'wpforo' ); ?> :</span>
220
221 <label for="<?php echo $this->get_field_id( 'forumids_filter' ) ?>_1"><?php _e( 'Yes', 'wpforo' ); ?></label>
222 <input id="<?php echo $this->get_field_id( 'forumids_filter' ) ?>_1" value="1" class="wpf_wdg_forumids_filter_1"
223 name="<?php echo esc_attr( $this->get_field_name( 'forumids_filter' ) ); ?>" <?php checked( $forumids_filter ); ?> type="radio">
224
225 <label for="<?php echo $this->get_field_id( 'forumids_filter' ) ?>_0"><?php _e( 'No', 'wpforo' ); ?></label>
226 <input id="<?php echo $this->get_field_id( 'forumids_filter' ) ?>_0" value="0" class="wpf_wdg_forumids_filter_0"
227 name="<?php echo esc_attr( $this->get_field_name( 'forumids_filter' ) ); ?>" <?php checked( $forumids_filter, false ); ?> type="radio">
228
229 <div class="wpf_wdg_forumids_wrap">
230 <label for="<?php echo $this->get_field_id( 'forumids' ) ?>"></label>
231 <select id="<?php echo $this->get_field_id( 'forumids' ) ?>" class="wpf_wdg_forumids" name="<?php echo esc_attr( $this->get_field_name( 'forumids' ) ); ?>[]" multiple>
232 <?php WPF()->forum->tree( 'select_box', false, $selected ) ?>
233 </select>
234 </div>
235 </div>
236 <p>
237 <span><?php _e( 'Autofilter by current forum', 'wpforo' ); ?> : </span>
238
239 <label for="<?php echo $this->get_field_id( 'current_forumid_filter' ) ?>_1"><?php _e( 'Yes', 'wpforo' ); ?></label>
240 <input id="<?php echo $this->get_field_id( 'current_forumid_filter' ) ?>_1" name="<?php echo esc_attr( $this->get_field_name( 'current_forumid_filter' ) ); ?>" value="1" <?php checked(
241 $current_forumid_filter
242 ); ?> type="radio">
243
244 <label for="<?php echo $this->get_field_id( 'current_forumid_filter' ) ?>_0"><?php _e( 'No', 'wpforo' ); ?></label>
245 <input id="<?php echo $this->get_field_id( 'current_forumid_filter' ) ?>_0" name="<?php echo esc_attr( $this->get_field_name( 'current_forumid_filter' ) ); ?>" value="0" <?php checked(
246 $current_forumid_filter,
247 false
248 ); ?> type="radio">
249 </p>
250 <p>
251 <label for="<?php echo $this->get_field_id( 'orderby' ) ?>"><?php _e( 'Order by', 'wpforo' ); ?>:</label>
252 <select name="<?php echo esc_attr( $this->get_field_name( 'orderby' ) ); ?>"
253 id="<?php echo $this->get_field_id( 'orderby' ) ?>">
254 <?php foreach( $this->orderby_fields as $orderby_key => $orderby_field ) : ?>
255 <option value="<?php echo $orderby_key; ?>"<?php echo( $orderby_key == $orderby ? ' selected' : '' ); ?>><?php echo $orderby_field; ?></option>
256 <?php endforeach; ?>
257 </select>
258 <label>
259 <select name="<?php echo esc_attr( $this->get_field_name( 'order' ) ); ?>">
260 <?php foreach( $this->order_fields as $order_key => $order_field ) : ?>
261 <option value="<?php echo $order_key; ?>"<?php echo( $order_key == $order ? ' selected' : '' ); ?>><?php echo $order_field; ?></option>
262 <?php endforeach; ?>
263 </select>
264 </label>
265 </p>
266 <p>
267 <label for="<?php echo $this->get_field_id( 'count' ) ?>"><?php _e( 'Number of Items', 'wpforo' ); ?></label>&nbsp;
268 <input id="<?php echo $this->get_field_id( 'count' ) ?>" type="number" min="1" style="width: 53px;" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>"
269 value="<?php echo esc_attr( $count ); ?>">
270 </p>
271 <p>
272 <span><?php _e( 'Display with avatars', 'wpforo' ); ?> : </span>
273
274 <label for="<?php echo $this->get_field_id( 'display_avatar' ) ?>_1"><?php _e( 'Yes', 'wpforo' ); ?></label>
275 <input id="<?php echo $this->get_field_id( 'display_avatar' ) ?>_1" value="1" <?php checked( $display_avatar ); ?> type="radio"
276 name="<?php echo esc_attr( $this->get_field_name( 'display_avatar' ) ); ?>">
277
278 <label for="<?php echo $this->get_field_id( 'display_avatar' ) ?>_0"><?php _e( 'No', 'wpforo' ); ?></label>
279 <input id="<?php echo $this->get_field_id( 'display_avatar' ) ?>_0" value="0" <?php checked( $display_avatar, false ); ?> type="radio"
280 name="<?php echo esc_attr( $this->get_field_name( 'display_avatar' ) ); ?>">
281 </p>
282 <p>
283 <span><?php _e( 'Refer topics to first unread post', 'wpforo' ); ?> : </span>
284
285 <label for="<?php echo $this->get_field_id( 'goto_unread' ) ?>_1"><?php _e( 'Yes', 'wpforo' ); ?></label>
286 <input id="<?php echo $this->get_field_id( 'goto_unread' ) ?>_1" <?php checked( $goto_unread ); ?> type="radio"
287 name="<?php echo esc_attr( $this->get_field_name( 'goto_unread' ) ); ?>">
288
289 <label for="<?php echo $this->get_field_id( 'goto_unread' ) ?>_0"><?php _e( 'No', 'wpforo' ); ?></label>
290 <input id="<?php echo $this->get_field_id( 'goto_unread' ) ?>_0" <?php checked( $goto_unread, false ); ?> type="radio"
291 name="<?php echo esc_attr( $this->get_field_name( 'goto_unread' ) ); ?>">
292 </p>
293 <p>
294 <label for="<?php echo $this->get_field_id( 'refresh_interval' ) ?>"><?php _e( 'Auto Refresh Interval Seconds', 'wpforo' ); ?></label>&nbsp;
295 <input id="<?php echo $this->get_field_id( 'refresh_interval' ) ?>" type="number" min="0" style="display: inline-block; width: 53px;"
296 name="<?php echo esc_attr( $this->get_field_name( 'refresh_interval' ) ); ?>" value="<?php echo esc_attr( $refresh_interval ); ?>">
297 <span style="color: #ccc"><?php _e( 'Set 0 to disable autorefresh', 'wpforo' ) ?></span>
298 </p>
299 </div>
300 <?php
301 }
302
303 public function update( $new_instance, $old_instance ) {
304 $new_instance = wpforo_parse_args( $new_instance, $this->default_instance );
305 $instance = [];
306 $instance['title'] = strip_tags( (string) $new_instance['title'] );
307 $instance['boardid'] = (int) $new_instance['boardid'];
308 $instance['forumids_filter'] = (bool) (int) $new_instance['forumids_filter'];
309 $instance['forumids'] = array_unique( array_filter( array_map( 'intval', (array) $new_instance['forumids'] ) ) );
310 $instance['orderby'] = ( ! empty( $new_instance['orderby'] ) && key_exists(
311 $new_instance['orderby'],
312 $this->orderby_fields
313 ) ) ? $new_instance['orderby'] : $this->default_instance['orderby'];
314 $instance['order'] = ( ! empty( $new_instance['order'] ) && key_exists(
315 $new_instance['order'],
316 $this->order_fields
317 ) ) ? $new_instance['order'] : $this->default_instance['order'];
318 $instance['count'] = (int) $new_instance['count'];
319 $instance['display_avatar'] = (bool) (int) $new_instance['display_avatar'];
320 $instance['current_forumid_filter'] = (bool) (int) $new_instance['current_forumid_filter'];
321 $instance['goto_unread'] = (bool) (int) $new_instance['goto_unread'];
322 $instance['refresh_interval'] = (int) $new_instance['refresh_interval'];
323
324 return $instance;
325 }
326
327 public function get_forum_tree() {
328 ob_start();
329 WPF()->forum->tree( 'select_box', false, [] );
330 wp_send_json_success( [ 'html' => ob_get_clean() ] );
331 }
332 }
333