PluginProbe
wpForo Forum / 2.3.1
wpForo Forum v2.3.1
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 / classes / Permissions.php

Permissions.php in wpForo Forum 2.3.1, at classes/Permissions.php

465 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\classes;
4
5 use stdClass;
6 use WP_User;
7
8 // Exit if accessed directly
9 if( ! defined( 'ABSPATH' ) ) exit;
10
11 class Permissions {
12 public $default;
13 public $accesses;
14 public $cans;
15
16 function __construct() {
17 $this->init_defaults();
18 $this->init_cans();
19 $this->init();
20 add_action( 'wpforo_after_init_classes', function(){
21 if( WPF()->is_installed() ) $this->init_current_user_accesses();
22 });
23 }
24
25 private function init_defaults() {
26 $this->default = new stdClass;
27 $this->default->access = [
28 'accessid' => 0,
29 'access' => '',
30 'title' => '',
31 'cans' => '',
32 ];
33 $this->default->cans = [
34 'vf' => __( 'Can view forum', 'wpforo' ),
35 'enf' => __( 'Can enter forum', 'wpforo' ),
36 'ct' => __( 'Can create topic', 'wpforo' ),
37 'vt' => __( 'Can view topic', 'wpforo' ),
38 'ent' => __( 'Can enter topic', 'wpforo' ),
39 'et' => __( 'Can edit topic', 'wpforo' ),
40 'dt' => __( 'Can delete topic', 'wpforo' ),
41 'cr' => __( 'Can post reply', 'wpforo' ),
42 'ocr' => __( 'Can reply to own topic', 'wpforo' ),
43 'vr' => __( 'Can view replies', 'wpforo' ),
44 'er' => __( 'Can edit replies', 'wpforo' ),
45 'dr' => __( 'Can delete replies', 'wpforo' ),
46 'eot' => __( 'Can edit own topic', 'wpforo' ),
47 'eor' => __( 'Can edit own reply', 'wpforo' ),
48 'dot' => __( 'Can delete own topic', 'wpforo' ),
49 'dor' => __( 'Can delete own reply', 'wpforo' ),
50 'tag' => __( 'Can add tags', 'wpforo' ),
51 'sb' => __( 'Can subscribe', 'wpforo' ),
52 'l' => __( 'Can like', 'wpforo' ),
53 'r' => __( 'Can report', 'wpforo' ),
54 's' => __( 'Can set topic sticky', 'wpforo' ),
55 'p' => __( 'Can set topic private', 'wpforo' ),
56 'op' => __( 'Can set own topic private', 'wpforo' ),
57 'vp' => __( 'Can view private topic', 'wpforo' ),
58 'au' => __( 'Can approve/unapprove content', 'wpforo' ),
59 'sv' => __( 'Can set topic solved', 'wpforo' ),
60 'osv' => __( 'Can set own topic solved', 'wpforo' ),
61 'v' => __( 'Can vote', 'wpforo' ),
62 'vop' => __( 'Can leave voice posts', 'wpforo' ),
63 'vlp' => __( 'Can listen voice posts', 'wpforo' ),
64 'a' => __( 'Can attach file', 'wpforo' ),
65 'va' => __( 'Can view attached files', 'wpforo' ),
66 'at' => __( 'Can set topic answered', 'wpforo' ),
67 'oat' => __( 'Can set own topic answered', 'wpforo' ),
68 'aot' => __( 'Can answer own question', 'wpforo' ),
69 'cot' => __( 'Can close topic', 'wpforo' ),
70 'mt' => __( 'Can move topic', 'wpforo' ),
71 'ccp' => __( 'Can create poll', 'wpforo' ),
72 'cvp' => __( 'Can vote poll', 'wpforo' ),
73 'cvpr' => __( 'Can view poll result', 'wpforo' ),
74 ];
75 }
76
77 private function init_cans() {
78 $this->cans = apply_filters( 'wpforo_init_cans', $this->default->cans );
79 }
80
81 private function init() {
82 if( WPF()->is_installed() ) {
83 if( $accesses = $this->get_accesses() ) {
84 foreach( $accesses as $access ) {
85 $this->accesses[ intval( $access['accessid'] ) ] = $this->accesses[ $access['access'] ] = $access;
86 }
87 }
88 }
89 }
90
91 private function init_current_user_accesses() {
92 WPF()->current_user_accesses = $this->get_forum_accesses_by_usergroup();
93 }
94
95 public function fix_access( $access ) {
96 $access = wpforo_array_args_cast_and_merge( (array) $access, $this->default->access );
97 $cans = array_map( '__return_zero', $this->cans );
98 $access['cans'] = maybe_unserialize( $access['cans'] );
99 if( is_array( $access['cans'] ) ) {
100 $access['cans'] = wpforo_array_args_cast_and_merge( $access['cans'], $cans );
101 } else {
102 $access['cans'] = $cans;
103 }
104
105 return $access;
106 }
107
108 /**
109 *
110 * @param string|int $access
111 *
112 * @return array access row by access key
113 */
114 function get_access( $access ) {
115 if( is_numeric( $access ) ) {
116 $access = intval( $access );
117 } else {
118 $access = sanitize_text_field( $access );
119 }
120 if( ! empty( $this->accesses[ $access ] ) ) return $this->accesses[ $access ];
121
122 $sql = "SELECT * FROM " . WPF()->tables->accesses;
123 if( is_int( $access ) ) {
124 $sql .= " WHERE `accessid` = %d";
125 } else {
126 $sql .= " WHERE `access` = %s";
127 }
128
129 return $this->fix_access( WPF()->db->get_row( WPF()->db->prepare( $sql, $access ), ARRAY_A ) );
130 }
131
132
133 /**
134 * get all accesses from accesses table
135 *
136 * @return array|null
137 */
138 function get_accesses() {
139 $sql = "SELECT * FROM " . WPF()->tables->accesses . " ORDER BY `accessid`";
140
141 return array_map( [$this, 'fix_access'], WPF()->db->get_results( $sql, ARRAY_A ) );
142 }
143
144 /**
145 * @param array $access
146 *
147 * @return int|bool inserted id or false
148 */
149 function add( $access ) {
150 if( !($access['title'] = sanitize_text_field( $access['title'] )) ){
151 WPF()->notice->add( 'Access title is empty', 'error' );
152
153 return false;
154 }
155
156 if( ! $access['access'] ) $access['access'] = uniqid();
157
158 $i = 2;
159 $slug = $access['access'];
160 while( WPF()->db->get_var( WPF()->db->prepare( "SELECT `access` FROM " . WPF()->tables->accesses . " WHERE `access` = %s", sanitize_text_field( $slug ) ) ) ) {
161 $slug = $access['access'] . '-' . $i;
162 $i ++;
163 }
164
165 if(
166 WPF()->db->insert(
167 WPF()->tables->accesses,
168 [
169 'title' => $access['title'],
170 'access' => sanitize_text_field( $slug ),
171 'cans' => serialize( $access['cans'] ),
172 ],
173 [ '%s', '%s', '%s', ]
174 )
175 ) {
176 $access['accessid'] = WPF()->db->insert_id;
177 WPF()->notice->add( 'Access successfully added', 'success' );
178
179 return $access['accessid'];
180 }
181
182 WPF()->notice->add( 'Access add error', 'error' );
183
184 return false;
185 }
186
187 /**
188 * @param array $access
189 *
190 * @return bool|int edited id or false
191 */
192 function edit( $access ) {
193 if( false !== WPF()->db->update( WPF()->tables->accesses, [
194 'title' => sanitize_text_field( $access['title'] ),
195 'cans' => serialize( $access['cans'] ),
196 ], [
197 'accessid' => $access['accessid'],
198 ], [ '%s', '%s' ], [ '%d' ] ) ) {
199 WPF()->notice->add( 'Access successfully edited', 'success' );
200
201 return $access['accessid'];
202 }
203
204 WPF()->notice->add( 'Access edit error', 'error' );
205
206 return false;
207 }
208
209 /**
210 * @param int $accessid
211 *
212 * @return bool|int deleted id or false
213 */
214 function delete( $accessid ) {
215 $accessid = intval( $accessid );
216 if( ! $accessid ) {
217 WPF()->notice->add( 'Access delete error', 'error' );
218
219 return false;
220 }
221
222 if( false !== WPF()->db->delete( WPF()->tables->accesses, [ 'accessid' => $accessid ], [ '%d' ] ) ) {
223 WPF()->notice->add( 'Access successfully deleted', 'success' );
224
225 return $accessid;
226 }
227
228 WPF()->notice->add( 'Access delete error', 'error' );
229
230 return false;
231 }
232
233 function forum_can( $do, $forumid = null, $groupids = null ) {
234 /**
235 * filter for other add-ons to manage can_attach bool value.
236 * e.g. PM add-on attachment function.
237 */
238 $filter_forum_can = apply_filters( 'wpforo_permissions_forum_can', null, $do, $forumid, $groupids );
239 if( ! is_null( $filter_forum_can ) ) return (int) (bool) $filter_forum_can;
240
241 if( ( is_null( $groupids ) && !WPF()->current_user_groupids ) || !$do ) return 0;
242
243 //User Forum accesses from Current Object of Current user
244 if( is_null( $groupids ) && WPF()->current_user_accesses ) {
245 $forum_id = (int) (is_null( $forumid ) ? wpfval( WPF()->current_object, 'forum', 'forumid' ) : ( wpfkey( $forumid, 'forumid' ) ? $forumid['forumid'] : $forumid ));
246 if( $forum_id && ($forum_accesses = wpfval( WPF()->current_user_accesses, $forum_id )) ) {
247 foreach( $forum_accesses as $cans ) {
248 if( (int) wpfval( $cans, $do ) ) return 1;
249 }
250 }
251
252 return 0;
253 }
254
255 //Use Custom User Forum Accesses
256 $forum = is_null( $forumid ) ? WPF()->current_object['forum'] : ( !wpfkey( $forumid, 'forumid' ) ? WPF()->forum->get_forum( $forumid ) : $forumid );
257 if( $forum ) {
258 $permissions = maybe_unserialize( $forum['permissions'] );
259 if( is_null( $groupids ) ) $groupids = WPF()->current_user_groupids;
260 $groupids = array_map( 'intval', (array) $groupids );
261 foreach( $groupids as $groupid ) {
262 if( $_access = wpfval( $permissions, $groupid ) ) {
263 $access = $this->get_access( $_access );
264 if( (int) wpfval( $access, 'cans', $do ) ) return 1;
265 }
266 }
267 }
268
269 return 0;
270 }
271
272 function user_can_manage_user( $user_id, $managing_user_id ) {
273 if( ! $user_id || ! $managing_user_id ) return false;
274 if( $user_id == $managing_user_id ) return true;
275
276 $user = new WP_User( $user_id );
277 $user_level = $this->user_wp_level( $user );
278 if( ! empty( $user->roles ) && is_array( $user->roles ) ) $user_role = array_shift( $user->roles );
279
280 $managing_user = new WP_User( $managing_user_id );
281 $managing_user_level = $this->user_wp_level( $managing_user );
282 if( ! empty( $managing_user->roles ) && is_array( $managing_user->roles ) ) $managing_user_role = array_shift( $managing_user->roles );
283
284 if( (int) $user_level > (int) $managing_user_level ) {
285 return true;
286 } elseif( $user_id == 1 && $user_role === 'administrator' ) {
287 return true;
288 } elseif( (int) $user_level === (int) $managing_user_level ) {
289 $member = WPF()->member->get_member( $user_id );
290 $managing_member = WPF()->member->get_member( $managing_user_id );
291 $user_wpforo_can = WPF()->usergroup->can( 'em', $member['groupids'] );
292 $managing_user_wpforo_can = WPF()->usergroup->can( 'em', $managing_member['groupids'] );
293 if( $user_wpforo_can && ! $managing_user_wpforo_can ) {
294 return true;
295 } else {
296 return false;
297 }
298 } elseif( $user_id != 1 && $managing_user_id == 1 && $managing_user_role === 'administrator' ) {
299 return false;
300 } else {
301 return false;
302 }
303 }
304
305 function user_wp_level( $user_object ) {
306 $level = 0;
307 $levels = [];
308 if( is_int( $user_object ) ) {
309 $user_object = new WP_User( $user_object );
310 }
311 if( isset( $user_object->allcaps ) && is_array( $user_object->allcaps ) && ! empty( $user_object->allcaps ) ) {
312 foreach( $user_object->allcaps as $level_key => $level_value ) {
313 if( strpos( (string) $level_key, 'level_' ) !== false && $level_value == 1 ) {
314 $levels[] = intval( str_replace( 'level_', '', $level_key ) );
315 }
316 }
317 if( ! empty( $levels ) ) {
318 $level = max( $levels );
319 }
320 }
321
322 return $level;
323 }
324
325 function can_edit_user( $userid ) {
326 if( ! $userid ) return false;
327 if( ! $this->user_can_edit_account( $userid ) ) {
328 WPF()->notice->clear();
329 WPF()->notice->add( 'Permission denied', 'error' );
330 wp_safe_redirect( wpforo_get_request_uri() );
331 exit();
332 }
333
334 return true;
335 }
336
337 public function can_link() {
338 if( ! WPF()->usergroup->can( 'em' ) ) {
339 $posts = WPF()->member->member_approved_posts( WPF()->current_userid );
340 $posts = intval( $posts );
341 if( ( $min_posts = wpforo_setting( 'antispam', 'min_number_posts_to_link' ) ) && $posts <= $min_posts ) return false;
342 }
343
344 return true;
345 }
346
347 public function can_attach( $forumid = null ) {
348 if( ! $forumid ) $forumid = null;
349
350 /**
351 * filter for other add-ons to manage can_attach bool value.
352 * e.g. PM add-on attachment function.
353 */
354 $filter_wpforo_can_attach = apply_filters( 'wpforo_can_attach', null, $forumid );
355 if( ! is_null( $filter_wpforo_can_attach ) ) return (bool) $filter_wpforo_can_attach;
356
357 if( ! $this->forum_can( 'a', $forumid ) ) return false;
358 if( ! WPF()->usergroup->can( 'em' ) ) {
359 $posts = WPF()->member->member_approved_posts( WPF()->current_userid );
360 $posts = intval( $posts );
361 if( ( $min_posts = wpforo_setting( 'antispam', 'min_number_posts_to_attach' ) ) && $posts <= $min_posts ) return false;
362 }
363
364 return true;
365 }
366
367 public function can_attach_file_type( $ext = '' ) {
368 if( ! WPF()->usergroup->can( 'em' ) && WPF()->member->current_user_is_new() && in_array( $ext, wpforo_setting( 'antispam', 'limited_file_ext' ) ) ) return false;
369
370 return true;
371 }
372
373 /**
374 * @return bool
375 */
376 public function can_post_now() {
377 if( wpforo_is_admin() || ( defined( 'IS_GO2WPFORO' ) && IS_GO2WPFORO ) ) {
378 return true;
379 }
380
381 $email = ( ( $userid = WPF()->current_userid ) ? '' : WPF()->current_user_email );
382 $groupid = WPF()->current_user_groupid;
383 if( WPF()->member->current_user_is_new() ) {
384 $groupid = 0;
385 }
386 if( ! $flood_interval = WPF()->usergroup->get_flood_interval( $groupid ) ) {
387 return true;
388 }
389 $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
390
391 $args = [
392 'userid' => $userid,
393 'email' => $email,
394 'orderby' => '`created` DESC, `postid` DESC',
395 'row_count' => 1,
396 'where' => "`created` >= '$hour_ago'",
397 ];
398 $items_count = 0;
399 $lastpost = WPF()->post->get_posts( $args, $items_count, false );
400 if( $lasttime = wpfval( $lastpost, 0, 'created' ) ) {
401 $lasttime = strtotime( $lasttime . ' GMT' );
402 $nowtime = time();
403 $diff = $nowtime - $lasttime;
404 if( $diff < $flood_interval ) {
405 return false;
406 }
407 }
408
409 return true;
410 }
411
412 public function get_forum_accesses_by_usergroup( $groupids = [] ) {
413 $forum_accesses = [];
414 if( !$groupids ) $groupids = WPF()->current_user_groupids;
415 if( ($groupids = array_map( 'intval', (array) $groupids )) && ($forums = WPF()->forum->get_forums()) ) {
416 foreach( $forums as $forum ) {
417 if( $permissions = maybe_unserialize( $forum['permissions'] ) ) {
418 foreach( $groupids as $groupid ) {
419 $access = wpfval( $permissions, $groupid );
420 if( $_access = $this->get_access( $access ) ){
421 if( $cans = wpfval( $_access, 'cans' ) ){
422 if( ! wpfkey( $forum_accesses, $forum['forumid'], $access ) ) $forum_accesses[ $forum['forumid'] ][ $access ] = $cans;
423 }
424 }
425 }
426 }
427 }
428 }
429
430 return apply_filters( 'wpforo_permissions_forum_accesses_by_usergroup', $forum_accesses, $groupids );
431 }
432
433 public function show_accesses_selectbox( $selected = [], $exclude = [] ) {
434 $accesses = $this->get_accesses();
435 foreach( $accesses as $accesse ) {
436 if( in_array( $accesse['access'], (array) $exclude ) ) continue;
437 printf(
438 '<option value="%1$s" %2$s>%3$s</option>',
439 esc_attr( $accesse['access'] ),
440 in_array( $accesse['access'], (array) $selected ) ? 'selected' : '',
441 esc_html( $accesse['title'] )
442 );
443 }
444 }
445
446 /**
447 * @param array|int $owner
448 * @param array|int $user
449 *
450 * @return bool
451 */
452 public function user_can_edit_account( $owner = [], $user = [] ) {
453 if( ! $user ) $user = WPF()->current_user;
454 if( ! $owner ) $owner = WPF()->current_object['user'];
455 if( is_numeric( $owner ) ) $owner = WPF()->member->get_member( $owner );
456 if( is_numeric( $user ) ) $user = WPF()->member->get_member( $user );
457 if( ! $user || ! $owner ) return false;
458 $is_users_same = wpforo_is_users_same( $user, $owner );
459 return wpforo_user_is( $user['userid'], 'admin' )
460 || ( WPF()->usergroup->can( 'em', $user['groupids'] ) && $this->user_can_manage_user( $user['userid'], $owner['userid'] ) )
461 || ( $is_users_same && wpforo_user_is( $user['userid'], 'moderator' ) )
462 || ( $is_users_same && $user['posts'] >= wpforo_setting( 'antispam', 'min_number_posts_to_edit_account' ) );
463 }
464 }
465