1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* Roles and Capabilities |
4
|
|
|
* |
5
|
|
|
* @package GravityView |
6
|
|
|
* @license GPL2+ |
7
|
|
|
* @since 1.14 |
8
|
|
|
* @author Katz Web Services, Inc. |
9
|
|
|
* @link http://gravityview.co |
10
|
|
|
* @copyright Copyright 2015, Katz Web Services, Inc. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
// Exit if accessed directly |
14
|
|
|
defined( 'ABSPATH' ) || exit; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* GravityView Roles Class |
18
|
|
|
* |
19
|
|
|
* This class handles the role creation and assignment of capabilities for those roles. |
20
|
|
|
* |
21
|
|
|
* @since 1.15 |
22
|
|
|
*/ |
23
|
|
|
class GravityView_Roles_Capabilities { |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var GravityView_Roles_Capabilities|null |
27
|
|
|
*/ |
28
|
|
|
static $instance = null; |
|
|
|
|
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @since 1.15 |
32
|
|
|
* @return GravityView_Roles_Capabilities |
33
|
|
|
*/ |
34
|
1 |
|
public static function get_instance() { |
35
|
|
|
|
36
|
1 |
|
if( ! self::$instance ) { |
37
|
|
|
self::$instance = new self; |
38
|
|
|
} |
39
|
|
|
|
40
|
1 |
|
return self::$instance; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Get things going |
45
|
|
|
* |
46
|
|
|
* @since 1.15 |
47
|
|
|
*/ |
48
|
|
|
public function __construct() { |
49
|
|
|
$this->add_hooks(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @since 1.15 |
54
|
|
|
*/ |
55
|
|
|
private function add_hooks() { |
56
|
|
|
add_filter( 'members_get_capabilities', array( 'GravityView_Roles_Capabilities', 'merge_with_all_caps' ) ); |
57
|
|
|
add_action( 'members_register_cap_groups', array( $this, 'members_register_cap_group' ), 20 ); |
58
|
|
|
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 ); |
59
|
|
|
add_action( 'admin_init', array( $this, 'add_caps') ); |
|
|
|
|
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Add support for `gravityview_full_access` capability, and |
65
|
|
|
* |
66
|
|
|
* @see map_meta_cap() |
67
|
|
|
* |
68
|
|
|
* @since 1.15 |
69
|
|
|
* |
70
|
|
|
* @param array $allcaps An array of all the user's capabilities. |
|
|
|
|
71
|
|
|
* @param array $caps Actual capabilities for meta capability. |
72
|
|
|
* @param array $args Optional parameters passed to has_cap(), typically object ID. |
73
|
|
|
* @param WP_User|null $user The user object, in WordPress 3.7.0 or higher |
74
|
|
|
* |
75
|
|
|
* @return mixed |
76
|
|
|
*/ |
77
|
7 |
|
public function filter_user_has_cap( $usercaps = array(), $caps = array(), $args = array(), $user = NULL ) { |
|
|
|
|
78
|
|
|
|
79
|
|
|
// Empty caps_to_check array |
80
|
7 |
|
if( ! $usercaps || ! $caps ) { |
|
|
|
|
81
|
4 |
|
return $usercaps; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Enable all GravityView caps_to_check if `gravityview_full_access` is enabled |
86
|
|
|
*/ |
87
|
7 |
|
if( ! empty( $usercaps['gravityview_full_access'] ) ) { |
88
|
|
|
|
89
|
3 |
|
$all_gravityview_caps = self::all_caps(); |
90
|
|
|
|
91
|
3 |
|
foreach( $all_gravityview_caps as $gv_cap ) { |
92
|
3 |
|
$usercaps[ $gv_cap ] = true; |
93
|
|
|
} |
94
|
|
|
|
95
|
3 |
|
unset( $all_gravityview_caps ); |
96
|
|
|
} |
97
|
|
|
|
98
|
7 |
|
$usercaps = $this->add_gravity_forms_usercaps_to_gravityview_caps( $usercaps ); |
99
|
|
|
|
100
|
7 |
|
return $usercaps; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* If a user has been assigned custom capabilities for Gravity Forms, but they haven't been assigned similar abilities |
105
|
|
|
* in GravityView yet, we give temporary access to the permissions, until they're set. |
106
|
|
|
* |
107
|
|
|
* This is for custom roles that GravityView_Roles_Capabilities::add_caps() doesn't modify. If you have a |
108
|
|
|
* custom role with the ability to edit any Gravity Forms entries (`gravityforms_edit_entries`), you would |
109
|
|
|
* expect GravityView to match that capability, until the role has been updated with GravityView caps. |
110
|
|
|
* |
111
|
|
|
* @since 1.15 |
112
|
|
|
* |
113
|
|
|
* @param array $usercaps User's allcaps array |
114
|
|
|
* |
115
|
|
|
* @return array |
116
|
|
|
*/ |
117
|
7 |
|
private function add_gravity_forms_usercaps_to_gravityview_caps( $usercaps ) { |
118
|
|
|
|
119
|
|
|
$gf_to_gv_caps = array( |
120
|
7 |
|
'gravityforms_edit_entries' => 'gravityview_edit_others_entries', |
121
|
|
|
'gravityforms_delete_entries' => 'gravityview_delete_others_entries', |
122
|
|
|
'gravityforms_view_entry_notes' => 'gravityview_view_entry_notes', |
123
|
|
|
'gravityforms_edit_entry_notes' => 'gravityview_delete_entry_notes', |
124
|
|
|
); |
125
|
|
|
|
126
|
7 |
|
foreach ( $gf_to_gv_caps as $gf_cap => $gv_cap ) { |
127
|
7 |
|
if ( isset( $usercaps[ $gf_cap ] ) && ! isset( $usercaps[ $gv_cap ] ) ) { |
128
|
7 |
|
$usercaps[ $gv_cap ] = $usercaps[ $gf_cap ]; |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
7 |
|
return $usercaps; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Add GravityView group to Members 1.x plugin management screen |
137
|
|
|
* @see members_register_cap_group() |
138
|
|
|
* @since 1.15 |
139
|
|
|
* @return void |
140
|
|
|
*/ |
141
|
|
|
function members_register_cap_group() { |
|
|
|
|
142
|
|
|
if ( function_exists( 'members_register_cap_group' ) ) { |
143
|
|
|
|
144
|
|
|
$args = array( |
145
|
|
|
'label' => __( 'GravityView', 'gravityview' ), |
146
|
|
|
'icon' => 'gv-icon-astronaut-head', |
147
|
|
|
'caps' => self::all_caps(), |
148
|
|
|
'merge_added' => true, |
149
|
|
|
'diff_added' => false, |
150
|
|
|
); |
151
|
|
|
|
152
|
|
|
members_register_cap_group( 'gravityview', $args ); |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Merge capabilities array with GravityView capabilities |
158
|
|
|
* |
159
|
|
|
* @since 1.15 Used to add GravityView caps to the Members plugin |
160
|
|
|
* @param array $caps Existing capabilities |
161
|
|
|
* @return array Modified capabilities array |
162
|
|
|
*/ |
163
|
1 |
|
public static function merge_with_all_caps( $caps ) { |
164
|
|
|
|
165
|
1 |
|
$return_caps = array_merge( $caps, self::all_caps() ); |
166
|
|
|
|
167
|
1 |
|
return array_unique( $return_caps ); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Retrieves the global WP_Roles instance and instantiates it if necessary. |
172
|
|
|
* |
173
|
|
|
* @see wp_roles() This method uses the exact same code as wp_roles(), here for backward compatibility |
174
|
|
|
* |
175
|
|
|
* @global WP_Roles $wp_roles WP_Roles global instance. |
176
|
|
|
* |
177
|
|
|
* @return WP_Roles WP_Roles global instance if not already instantiated. |
178
|
|
|
*/ |
179
|
|
|
private function wp_roles() { |
180
|
|
|
global $wp_roles; |
|
|
|
|
181
|
|
|
|
182
|
|
|
if ( ! isset( $wp_roles ) ) { |
183
|
|
|
$wp_roles = new WP_Roles(); |
|
|
|
|
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
return $wp_roles; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Add capabilities to their respective roles if they don't already exist |
191
|
|
|
* This could be simpler, but the goal is speed. |
192
|
|
|
* |
193
|
|
|
* @since 1.15 |
194
|
|
|
* @return void |
195
|
|
|
*/ |
196
|
|
|
public function add_caps() { |
197
|
|
|
|
198
|
|
|
$wp_roles = $this->wp_roles(); |
|
|
|
|
199
|
|
|
|
200
|
|
|
if ( is_object( $wp_roles ) ) { |
201
|
|
|
|
202
|
|
|
$_use_db_backup = $wp_roles->use_db; |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* When $use_db is true, add_cap() performs update_option() every time. |
206
|
|
|
* We disable updating the database here, then re-enable it below. |
207
|
|
|
*/ |
208
|
|
|
$wp_roles->use_db = false; |
209
|
|
|
|
210
|
|
|
$capabilities = self::all_caps( false, false ); |
211
|
|
|
|
212
|
|
|
foreach ( $capabilities as $role_slug => $role_caps ) { |
213
|
|
|
foreach ( $role_caps as $cap ) { |
214
|
|
|
$wp_roles->add_cap( $role_slug, $cap ); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Update the option, as it does in add_cap when $use_db is true |
220
|
|
|
* |
221
|
|
|
* @see WP_Roles::add_cap() Original code |
222
|
|
|
*/ |
223
|
|
|
update_option( $wp_roles->role_key, $wp_roles->roles ); |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Restore previous $use_db setting |
227
|
|
|
*/ |
228
|
|
|
$wp_roles->use_db = $_use_db_backup; |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Get an array of GravityView capabilities |
234
|
|
|
* |
235
|
|
|
* @see get_post_type_capabilities() |
236
|
|
|
* |
237
|
|
|
* @since 1.15 |
238
|
|
|
* |
239
|
|
|
* @param string $single_role If set, get the caps_to_check for a specific role. Pass 'all' to get all caps_to_check in a flat array. Default: `all` |
240
|
|
|
* @param boolean $flat_array True: return all caps in a one-dimensional array. False: a multi-dimensional array with `$single_role` as keys and the caps as the values |
241
|
|
|
* |
242
|
|
|
* @return array If $role is set, flat array of caps_to_check. Otherwise, a multi-dimensional array of roles and their caps_to_check with the following keys: 'administrator', 'editor', 'author', 'contributor', 'subscriber' |
243
|
|
|
*/ |
244
|
8 |
|
public static function all_caps( $single_role = false, $flat_array = true ) { |
245
|
|
|
|
246
|
|
|
// Change settings |
247
|
|
|
$administrator_caps = array( |
248
|
8 |
|
'gravityview_full_access', // Grant access to all caps_to_check |
249
|
|
|
'gravityview_view_settings', |
250
|
|
|
'gravityview_edit_settings', |
251
|
|
|
'gravityview_uninstall', // Ability to trigger the Uninstall @todo |
252
|
|
|
'gravityview_contact_support', // Whether able to send a message to support via the Support Port |
253
|
|
|
); |
254
|
|
|
|
255
|
|
|
// Edit, publish, delete own and others' stuff |
256
|
|
|
$editor_caps = array( |
257
|
8 |
|
'edit_others_gravityviews', |
258
|
|
|
'read_private_gravityviews', |
259
|
|
|
'delete_private_gravityviews', |
260
|
|
|
'delete_others_gravityviews', |
261
|
|
|
'edit_private_gravityviews', |
262
|
|
|
'publish_gravityviews', |
263
|
|
|
'delete_published_gravityviews', |
264
|
|
|
'edit_published_gravityviews', |
265
|
|
|
'copy_gravityviews', // For duplicate/clone View functionality |
266
|
|
|
|
267
|
|
|
// GF caps_to_check |
268
|
|
|
'gravityview_edit_others_entries', |
269
|
|
|
'gravityview_moderate_entries', // Approve or reject entries from the Admin; show/hide approval column in Entries screen |
270
|
|
|
'gravityview_delete_others_entries', |
271
|
|
|
'gravityview_add_entry_notes', |
272
|
|
|
'gravityview_view_entry_notes', |
273
|
|
|
'gravityview_delete_entry_notes', |
274
|
|
|
'gravityview_email_entry_notes', |
275
|
|
|
); |
276
|
|
|
|
277
|
|
|
// Edit, delete own stuff |
278
|
|
|
$author_caps = array( |
279
|
|
|
// GF caps_to_check |
280
|
8 |
|
'gravityview_edit_entries', |
281
|
|
|
'gravityview_edit_entry', |
282
|
|
|
'gravityview_edit_form_entries', // This is similar to `gravityview_edit_entries`, but checks against a Form ID $object_id |
283
|
|
|
'gravityview_delete_entries', |
284
|
|
|
'gravityview_delete_entry', |
285
|
|
|
); |
286
|
|
|
|
287
|
|
|
// Edit and delete drafts but not publish |
288
|
|
|
$contributor_caps = array( |
289
|
8 |
|
'edit_gravityviews', // Affects if you're able to see the Views menu in the Admin, and also if you're able to override cache using ?nocache |
290
|
|
|
'delete_gravityviews', |
291
|
|
|
'gravityview_getting_started', // Getting Started page access |
292
|
|
|
'gravityview_support_port', // Display GravityView Support Port |
293
|
|
|
); |
294
|
|
|
|
295
|
|
|
// Read only |
296
|
|
|
$subscriber_caps = array( |
297
|
8 |
|
'gravityview_view_entries', |
298
|
|
|
'gravityview_view_others_entries', |
299
|
|
|
); |
300
|
|
|
|
301
|
8 |
|
$subscriber = $subscriber_caps; |
302
|
8 |
|
$contributor = array_merge( $contributor_caps, $subscriber_caps ); |
303
|
8 |
|
$author = array_merge( $author_caps, $contributor_caps, $subscriber_caps ); |
304
|
8 |
|
$editor = array_merge( $editor_caps, $author_caps, $contributor_caps, $subscriber_caps ); |
305
|
8 |
|
$administrator = array_merge( $administrator_caps, $editor_caps, $author_caps, $contributor_caps, $subscriber_caps ); |
306
|
8 |
|
$all = $administrator; |
307
|
|
|
|
308
|
|
|
// If role is set, return caps_to_check for just that role. |
309
|
8 |
|
if( $single_role ) { |
|
|
|
|
310
|
8 |
|
$caps = isset( ${$single_role} ) ? ${$single_role} : false; |
311
|
8 |
|
return $flat_array ? $caps : array( $single_role => $caps ); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
// Otherwise, return multi-dimensional array of all caps_to_check |
315
|
5 |
|
return $flat_array ? $all : compact( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Check whether the current user has a capability |
320
|
|
|
* |
321
|
|
|
* @since 1.15 |
322
|
|
|
* |
323
|
|
|
* @see WP_User::user_has_cap() |
324
|
|
|
* @see https://codex.wordpress.org/Plugin_API/Filter_Reference/user_has_cap You can filter permissions based on entry/View/form ID using `user_has_cap` filter |
325
|
|
|
* |
326
|
|
|
* @see GFCommon::current_user_can_any |
327
|
|
|
* @uses GFCommon::current_user_can_any |
328
|
|
|
* |
329
|
|
|
* @param string|array $caps_to_check Single capability or array of capabilities |
330
|
|
|
* @param int|null $object_id (optional) Parameter can be used to check for capabilities against a specific object, such as a post or us |
331
|
|
|
* @param int|null $user_id (optional) Check the capabilities for a user who is not necessarily the currently logged-in user |
332
|
|
|
* |
333
|
|
|
* @return bool True: user has at least one passed capability; False: user does not have any defined capabilities |
334
|
|
|
*/ |
335
|
8 |
|
public static function has_cap( $caps_to_check = '', $object_id = null, $user_id = null ) { |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* @filter `gravityview/capabilities/allow_logged_out` Shall we allow a cap check for non-logged in users? |
339
|
|
|
* |
340
|
|
|
* There are use-cases, albeit strange ones, where we'd like to check and override capabilities for |
341
|
|
|
* for a non-logged in user. |
342
|
|
|
* |
343
|
|
|
* Examples, you ask? https://github.com/gravityview/GravityView/issues/826 |
344
|
|
|
* |
345
|
|
|
* @param boolean $allow_logged_out Allow the capability check or bail without even checking. Default: false. Do not allow. Do not pass Go. Do not collect $200. |
346
|
|
|
* @param int|null $object_id (optional) Parameter can be used to check for capabilities against a specific object, such as a post or us. |
347
|
|
|
* @param int|null $user_id (optional) Check the capabilities for a user who is not necessarily the currently logged-in user. |
348
|
|
|
*/ |
349
|
8 |
|
$allow_logged_out = apply_filters( 'gravityview/capabilities/allow_logged_out', false, $caps_to_check, $object_id, $user_id ); |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* We bail with a negative response without even checking if: |
353
|
|
|
* |
354
|
|
|
* 1. The current user is not logged in and non-logged in users are considered unprivileged (@see `gravityview/capabilities/allow_logged_out` filter). |
355
|
|
|
* 2. If the caps to check are empty. |
356
|
|
|
*/ |
357
|
8 |
|
if ( ( ! is_user_logged_in() && ! $allow_logged_out ) || empty( $caps_to_check ) ) { |
358
|
1 |
|
return false; |
359
|
|
|
} |
360
|
|
|
|
361
|
8 |
|
$has_cap = false; |
362
|
|
|
|
363
|
|
|
// Add full access caps for GV & GF |
364
|
8 |
|
$caps_to_check = self::maybe_add_full_access_caps( $caps_to_check ); |
365
|
|
|
|
366
|
8 |
|
foreach ( $caps_to_check as $cap ) { |
367
|
8 |
|
if( ! is_null( $object_id ) ) { |
368
|
4 |
|
$has_cap = $user_id ? user_can( $user_id, $cap, $object_id ) : current_user_can( $cap, $object_id ); |
369
|
|
|
} else { |
370
|
5 |
|
$has_cap = $user_id ? user_can( $user_id, $cap ) : current_user_can( $cap ); |
371
|
|
|
} |
372
|
|
|
// At the first successful response, stop checking |
373
|
8 |
|
if( $has_cap ) { |
374
|
8 |
|
break; |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
|
378
|
8 |
|
return $has_cap; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* Add Gravity Forms and GravityView's "full access" caps when any other caps are checked against. |
383
|
|
|
* |
384
|
|
|
* @since 1.15 |
385
|
|
|
|
386
|
|
|
* @param array $caps_to_check |
387
|
|
|
* |
388
|
|
|
* @return array |
389
|
|
|
*/ |
390
|
5 |
|
public static function maybe_add_full_access_caps( $caps_to_check = array() ) { |
391
|
|
|
|
392
|
5 |
|
$caps_to_check = (array)$caps_to_check; |
|
|
|
|
393
|
|
|
|
394
|
5 |
|
$all_gravityview_caps = self::all_caps(); |
395
|
|
|
|
396
|
|
|
// Are there any $caps_to_check that are from GravityView? |
397
|
5 |
|
if( $has_gravityview_caps = array_intersect( $caps_to_check, $all_gravityview_caps ) ) { |
398
|
4 |
|
$caps_to_check[] = 'gravityview_full_access'; |
399
|
|
|
} |
400
|
|
|
|
401
|
5 |
|
$all_gravity_forms_caps = class_exists( 'GFCommon' ) ? GFCommon::all_caps() : array(); |
402
|
|
|
|
403
|
|
|
// Are there any $caps_to_check that are from Gravity Forms? |
404
|
5 |
|
if( $all_gravity_forms_caps = array_intersect( $caps_to_check, $all_gravity_forms_caps ) ) { |
405
|
4 |
|
$caps_to_check[] = 'gform_full_access'; |
406
|
|
|
} |
407
|
|
|
|
408
|
5 |
|
return array_unique( $caps_to_check ); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Remove all GravityView caps_to_check from all roles |
413
|
|
|
* |
414
|
|
|
* @since 1.15 |
415
|
|
|
* @return void |
416
|
|
|
*/ |
417
|
|
|
public function remove_caps() { |
418
|
|
|
|
419
|
|
|
$wp_roles = $this->wp_roles(); |
|
|
|
|
420
|
|
|
|
421
|
|
|
if ( is_object( $wp_roles ) ) { |
422
|
|
|
|
423
|
|
|
/** Remove all GravityView caps_to_check from all roles */ |
424
|
|
|
$capabilities = self::all_caps(); |
425
|
|
|
|
426
|
|
|
// Loop through each role and remove GV caps_to_check |
427
|
|
|
foreach( $wp_roles->get_names() as $role_slug => $role_name ) { |
428
|
|
|
foreach ( $capabilities as $cap ) { |
429
|
|
|
$wp_roles->remove_cap( $role_slug, $cap ); |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
add_action( 'init', array( 'GravityView_Roles_Capabilities', 'get_instance' ), 1 ); |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.