• Resolved Scott Bressler

    (@sbressler)


    This support topic is a follow-up to this Trac ticket. I think this is a more appropriate place to continue the discussion, since now it seems it will be more specific to my needs.

    Something in my plugin allows users of a certain role or higher to accomplish certain functionality. The minimum role required is set via a drop-down. Anyone with the cap manage_options can change what role or higher is checked against before allowing users into this functionality.

    At first I thought I could just check current_user_can(‘WHATEVER_ROLE_IS_SELECTED_AS_MIN’). However, current_user_can(‘editor’) fails for admins (on all but two production blogs – if you have any idea why it would succeed on some blogs (without role plugins), let me know). So that wouldn’t work. Now I have a few routes I could go:

    • I could check against the deprecated user levels (1 for contrib, 8+ for admin, etc.). That would give me exactly the functionality I want. However, then I’m using something deprecated.
    • I could alternatively pick a capability that each role has, and if that role is selected as the minimum, check against that capability.
    • I could loop through the roles in $wp_roles->get_names and allow all roles up to and including the selected role. However, that’s depending on the ordering of that array, which while it should be fine, probably isn’t dependable.
    • Finally, I could just create my own new capability and assign it to the minimum selected. However, that would make it slightly tricky to make sure the caps are all in place when a user changes the selection. Or if I just made my users install a role-management plugin, that would make their lives more difficult.

    Is there some ideal way of doing this? If I go with the 2nd solution, which capabilities should I be choosing to check against for each role?

    Thanks,
    Scott

Viewing 10 replies - 1 through 10 (of 10 total)
  • Something in my plugin allows users of a certain role or higher to accomplish certain functionality. The minimum role required is set via a drop-down. Anyone with the cap manage_options can change what role or higher is checked against before allowing users into this functionality.

    As hinted on the ticket, you need to make a choice. Either you need to assume that you’re dealing with the five default roles that are positively hierarchical, or you cannot make any assumptions at all. So:

    nacin_current_user_has_at_least( $role ) {
     	$the_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
    	foreach ( $the_roles as $the_role ) {
    		if ( current_user_can( $the_role ) )
    			return true;
    		if ( $role == $the_role )
    			break;
    	}
    	return false;
    }

    Note that this is just an example, and is not at all recommended. (Also not tested, but my logic looks correct.)

    Instead, I would offer checkboxes of which roles are allowed to perform an action. Perhaps then add a custom capability to said roles so you can just check the capability. Again, either the above function suffices, or if you want to support custom roles, you’ll need to try something different, as there is no guarantee they are hierarchical.

    Hope that helps –
    Nacin

    As nacin said, your assumption that roles are hierarchical is unfounded. They just happen to be hierarchical now, but might not be in the future.

    A role is just a collection of capabilities. Nothing more.

    Thread Starter Scott Bressler

    (@sbressler)

    Thanks for your help and advice, Andrew. I decided to go with creating my own capability. I’ve now added this to the settings page for the plugin, but the logic for adding or removing the capability from the roles based on the checkboxes obviously isn’t handled by the Settings API and my settings group. How and where should that be handled?

    Here is the code I have for displaying the checkboxes:

    <p>
      <strong>Roles that can view calendar</strong><br />
      <?php foreach($wp_roles->get_names() as $role => $role_name) :
        if ( $wp_roles->is_role( $role ) ) :
          $target_role =& get_role( $role );
          $role_has_cap = $target_role->has_cap( 'view_calendar' );
          ?>
          <label for="calendar_view_<?php echo $role; ?>">
            <input type="checkbox" id="calendar_view_<?php echo $role; ?>" value="<?php echo $role; ?>" <?php echo ($role_has_cap ? 'checked="yes"' : '');?> style="margin-bottom: 5px;" />
            <?php _e($role_name, 'edit-flow') ?>
          </label>
          <br />
        <?php endif; ?>
      <?php endforeach; ?>
      <span class="description"><?php _e('Select above which roles may view the Edit Flow Calendar.', 'edit-flow') ?></span>
    </p>

    All this code is in the callback for add_submenu_page which echos all the settings.

    Thanks in advance!

    -Scott

    Scott,
    There’s a few options there. If you are saving some other data to the options table on the same settings group, then a trick is to piggyback on the sanitization callback. Otherwise, you can always tap into load-$pagenow and just make sure you are checking nonces and such before proceeding.

    Few other notes:
    – Make sure you are escaping your output into attributes with esc_attr().
    – It’s actually checked=”checked” versus “yes”, and we have a helper function for it: checked( $value1, $value2 = true, $echo = true ). (Same with selected() and disabled()).

    Thread Starter Scott Bressler

    (@sbressler)

    Thanks for the head’s up about the checked method. I’ve used selected before, but forgot about it for this. Do I need to escape anything here? (Clearly I haven’t done too many forms.) I’m just showing one 6 checkboxes, one of which is based on an option (calendar_enabled) and the other 5 of which are based on capabilities. What could/should be escaped?

    Since the role-related checkboxes need to add/remove the capability from those roles, I’m not sure how I can piggyback, particularly given the haphazard usage of the Settings API for this page. Here’s the code (without any of this new stuff). I tried adding a validation method on line 143, but the data coming into there wasn’t an associative array, I assume because of the naming of the inputs in the form, and/or the half-baked use of the Settings API.

    So do I have to use nonces and load-$pagenow? If so, can you explain a bit more how I’d do that? I’ve used $pagenow to check which page I was on before, but never hooked into that filter.

    Thanks!

    -Scott

    I’d definitely use the callback. You just don’t have one yet. You should though — validating inputs coming through an options form is excellent for both stability and security.

    So, as you’ve found, register_setting takes an optional third argument, a callback. It’ll end up receiving an array, since you’re using a single DB field to store your options (my preferred way). Take a look at the last example in https://planetozh.com/blog/2009/05/handling-plugins-options-in-wordpress-28-with-register_setting/ to see how that would work.

    Does your settings page actually work? You only register a single option, yet your get_plugin_option_fullname() method returns namespaced options, when it should return a single option name with an associative key, like option_name_in_db[your_editflow_option]. Then option_name_in_db gets updated directly into the row in the database, and stores all of your settings.

    Also, I’d turn on WP_DEBUG. Looks like there’s some unchecked indexes in there. For example, if ( $_GET['updated'] == true)if $_GET['updated'] isn’t set, then you’ll be triggering a notice. Also, I’d replace that entire line with a single call to settings_errors(). (You need to call that yourself when you’re posting to options.php, as you should be, but you’re not actually a submenu page of options.php. Otherwise it gets called for you.)

    So yeah, take a look at how to formulate a validation callback, and then handle your role updates in there.

    If you want to see an extremely simple example of it all working together, here’s a setting coming from my Simple Footnotes plugin v 0.3:

    https://plugins.trac.www.ads-software.com/browser/simple-footnotes/tags/0.3/simple-footnotes.php?rev=266663

    First, I register the settings field and the setting in the last two lines of the admin_init() method.

    Note how the register_setting_cb() method is the sanitization callback. Though I’m saving only one setting via the form, I also have a db_version I use internally, so I add that in, and return the array. Both get stored in the same single setting called ‘simple_footnotes’.

    In settings_field_cb(), I then use simple_footnotes[placement] as the name of the input. (Not simple_footnotes_placement.) That’ll provide a nice array of ‘simple_footnotes’ options via $_POST that WordPress will then handle for you. Again note how I sanitized everything in register_setting_cb() — nothing can sneak into the DB I don’t want there, great for both security and stability.

    It would be in the register_setting_cb() that you can update your role as well.

    Thread Starter Scott Bressler

    (@sbressler)

    Thanks for all your feedback, Andrew! So I’ve concluded along with Mo who originally coded the settings page that the settings page in EF needs a good bit of work. The settings should be combined into one option field and there should be some sanitization. I already fully leverage the Settings API in my Media Credit plugin, but EF needs some similar refactoring. Thanks for the nudge.

    We’ve decided for this new capability not to offer any modification to the default roles to which we add the cap. I added a filter for which capability is actually checked against to view the calendar, but the only UI option we’ll offer is turning on/off the calendar. EF will likely offer many more cap-specific features in the future, at which point we may re-evaluate, but for now we’re just going to recommend installing a role plugin rather than trying to implement rudimentary role management.

    Thanks again for all your help!

    That sounds like a good approach.

    Cheers!

    well this was useful!!
    I used your function Andrew.
    I know I shouldn’t assume but to me it seems logical that at least an editor means that only an admin would be above (with regards to posting posts anyway).

    thanks for the function, I swiped it with an audible ‘yoink!’ ??

Viewing 10 replies - 1 through 10 (of 10 total)
  • The topic ‘Check user role is something or above’ is closed to new replies.