WordPress.org

Make WordPress Core

Opened 7 years ago

Last modified 5 weeks ago

#16841 assigned defect (bug)

Manually created user roles not showing in author dropdown regardless of assigned capabilities

Reported by: 10sexyapples Owned by: swissspidy
Milestone: 5.0 Priority: normal
Severity: normal Version: 3.1
Component: Role/Capability Keywords: needs-patch needs-unit-tests
Focuses: Cc:

Description (last modified by ocean90)

I posted the below on wp-testers hoping to verify or gain experiences from others in order to confirm this prior to reporting it as a bug, but, haven't received any further input, and it's just becoming more evident to me that perhaps it should be posted here, so, I hope I'm not creating a false report here. Please excuse me if I am.

I'm posting this as a link so that it will translate more clearly rather than just cluttering up this message with more text than necessary.
http://lists.automattic.com/pipermail/wp-testers/2011-March/014130.html

Upon further investigation I have also come across the following: #16451

I'm not an expert at debugging this sort of thing, but, I have attempted all within my knowledge thus far, and in looking at all of the returned objects and entries, the only difference I can spot between how the default user roles ( which show up properly in the dropdown ) and the custom created roles ( which do not ) are being returned is that the default user roles, editor for instance, correctly show the deprecated level_7 entry, and the custom user role ( with more assigned privileges than the editor ) does not show user levels in the capabilities array at all, and shows level_0 in the object.

This is leading me to think that perhaps there is something happening with those user levels in creating the author dropdown on the post editor that is blocking the custom user roles from showing up as expected.

When running the following code:

echo '<br /><br /><h3>Roles</h3>';
	foreach ( $wp_roles->role_names as $role => $name ) :
		echo '<br /> <br />';
		echo '<pre> Role displayed in Admin as ' . $name ;
		echo  '     Database entry: '  . $role . '</pre>';

		echo '<h5> Capabilities assigned to the role of ' . $name. '</h5>';
		// print_r( $caps);
		echo '<pre>';
		$rolename = get_role($role);
		$caps = $rolename->capabilities;
			foreach ($caps as $capability => $value):
				echo  $capability . ' '.  $value . "\n" ;
			endforeach;
		echo '</pre>';
	endforeach;

the default wp roles return the following capability value pairings

delete_published_posts 1

roles created with Justin Tadlock's members plugin return the following

delete_published_posts delete_published_posts

roles created manually in functions.php utilizing

add_role( 'test_role', 'Test Role' );

//saved and then removed before running the code below ...

$role = get_role('test_role');

$role->add_cap('read');
$role->add_cap('edit_posts);
$role->add_cap('edit_others_posts');
$role->add_cap('publish_posts');
$role->add_cap('read_private_posts');
$role->add_cap('delete_posts');
$role->add_cap('delete_private_posts');
$role->add_cap('delete_published_posts');
$role->add_cap('delete_others_posts');
$role->add_cap('edit_private_posts');
$role->add_cap('edit_published_posts');

return the following ...

delete_published_posts 1

but lack the user levels as mentioned above.

Let me know if there is any further info I can provide to help sort this out~

Change History (49)

#1 @10sexyapples
7 years ago

ahA! okay, I believe the use of user_level here is the culprit:
#15871

If user level is going to stick around in this way then shouldn't add_cap perhaps take this into consideration?

Last edited 7 years ago by scribu (previous) (diff)

#2 @scribu
7 years ago

  • Component changed from General to Role/Capability

The workaround would just be to add a 'level_1' cap to your role.

It's PITA, considering how user levels have been deprecated so long ago, but there you go.

#3 @scribu
7 years ago

PS: Instead of posting the full ticket url, just write #123, where 123 is the ticket number.

#4 @10sexyapples
7 years ago

Hmm. I actually tried adding the level caps, but, didn't see any change in the array after.
I was using add_cap.
Perhaps I saved, and then removed my code before it got picked up? I'm going to try it again.
And yes, definitely a PITA ... I saw your comments in having to put it there in the first place ...
As usual, thanks for the tips on proper posting etiquette ;-)
# 123 it is.

Last edited 7 years ago by 10sexyapples (previous) (diff)

#5 @scribu
7 years ago

Related: #16714

#6 @magicroundabout
6 years ago

Hello, this is my first post on Trac, so I hope it's useful.

I spotted that new users in roles added with add_role don't appear in the author dropdown list. This seems to have been introduced in #15871 when code was added to WP_User_Query->prepare_query to only select users with an old-school user-level of > 0 if 'who' => 'authors' was passed in the args.

I don't quite understand the discussion on the logic of this (in #15871). It seems to me that user levels are deprecated, so shouldn't this select users with the edit_posts capability?

Alternatively (additionally?), perhaps we could have a filter hook in wp_dropdown_users that lets us modify the arguments passed to get_users()?

#7 @scribu
6 years ago

We still use user levels there because we don't have a good way of getting the same list via capabilties. It's a mess.

#8 @lgladdy
6 years ago

And additional (imo) bug stops add_cap('level_1') from fixing this for existing users. I've posted a trac ticket for it #19747

Last edited 6 years ago by lgladdy (previous) (diff)

#9 @markoheijnen
6 years ago

  • Cc marko@… added

#10 @pauldewouters
6 years ago

  • Cc pauldewouters added

#11 @scottconnerly
5 years ago

  • Cc scott@… added

#12 @BandonRandon
5 years ago

  • Cc BandonRandon added

#13 @viniciusrtf
4 years ago

While trying to apply level_1 to my custom role, I didn't remember the custom role name (it is different from display name and it doesn't appear anywhere in backend), so I figured it out with this dirty SQL query:

SELECT meta_value FROM wp_usermeta WHERE user_id = user-id-here;
Last edited 4 years ago by viniciusrtf (previous) (diff)

#14 @jchristopher
4 years ago

  • Cc jonathan@… added

#15 @sterlo
3 years ago

To clarify, this is all tied into this particular section:

https://core.trac.wordpress.org/browser/tags/3.9.1/src/wp-includes/user.php#L649

$qv['meta_key'] = $wpdb->get_blog_prefix( $blog_id ) . 'user_level';
$qv['meta_value'] = 0;
$qv['meta_compare'] = '!=';

This will end up meaning "give me all users that have a wp_user_level that is not equal to 0".

If you go into MySQL and run something along the lines of this:

[mysql> select * from wp_usermeta WHERE user_id = 19;
+----------+---------+------------------------------+-------------------------------------------------------+
| umeta_id | user_id | meta_key                     | meta_value                                            |
+----------+---------+------------------------------+-------------------------------------------------------+
|      225 |      19 | first_name                   | John                                                 |
|      226 |      19 | last_name                    | Doe                                              |
|      227 |      19 | nickname                     | @jdoe                                            |
|      228 |      19 | description                  |                                                       |
|      229 |      19 | rich_editing                 | true                                                  |
|      230 |      19 | comment_shortcuts            | false                                                 |
|      231 |      19 | admin_color                  | fresh                                                 |
|      232 |      19 | use_ssl                      | 0                                                     |
|      233 |      19 | show_admin_bar_front         | true                                                  |
|      234 |      19 | wp_capabilities              | a:1:{s:15:"content-manager";b:1;}                     |
|      235 |      19 | wp_user_level                | 10                                                    |
|      236 |      19 | dismissed_wp_pointers        | wp350_media,wp360_revisions,wp360_locks,wp390_widgets |
|      429 |      19 | wpseo_title                  |                                                       |
|      430 |      19 | wpseo_metadesc               |                                                       |
|      431 |      19 | wpseo_metakey                |                                                       |
|      432 |      19 | _yoast_wpseo_profile_updated | 1400732591                                            |
|      433 |      19 | googleplus                   |                                                       |
|      434 |      19 | twitter                      |                                                       |
|      435 |      19 | facebook                     |                                                       |
+----------+---------+------------------------------+-------------------------------------------------------+
19 rows in set (0.00 sec)]

The line you need to pay attention to is:

|      235 |      19 | wp_user_level                | 10                                                    |

When you create your custom role, you need to add something that is > 0 in terms of wp_user_level.

Adjusting the role to increase the user_level is the end goal. To accomplish that you can use the $role->add_cap('level_1'); method explained by others - as long as it is not level_0.

It is important to note that you need to edit the user and re-save them with the add_cap change in place. This will update the database so that wp_user_level is no longer 0.

#16 @DrewAPicture
3 years ago

  • Summary changed from manually created user roles not showing in author dropdown irregardless of assigned capabilities to Manually created user roles not showing in author dropdown regardless of assigned capabilities

#17 @coolmann
3 years ago

Any chance to see this fixed in 4.1?

#18 @ckpicker
3 years ago

Would love to see this fixed in a future version.

#19 @juiceboxint
3 years ago

+1. 99.8% of users won't ever come across this, but it's a big deal to those of us who use WP as a serious CMS.

#20 follow-up: @lgladdy
3 years ago

The add_cap('level_1'); workaround will also apply to any existing users with the role you're adding to from r31190 / WordPress 4.2 which at least makes the workaround a little easier.

[Edit: Update typo - 4.2 not 3.2]

Last edited 3 years ago by lgladdy (previous) (diff)

#21 in reply to: ↑ 20 ; follow-up: @juiceboxint
3 years ago

Replying to lgladdy:

The add_cap('level_1'); workaround will also apply to any existing users with the role you're adding to from r31190 / WordPress 3.2 which at least makes the workaround a little easier.

Yep, the only thing is that you still have to change each user's role to something else and then back in order to update the actual user_level field in the database, if they were assigned that role before you added the level_1 cap. (tested a few minute ago on 4.1) So the workaround itself is pretty obscure, and the caveat for the workaround is even more obscure.

#22 in reply to: ↑ 21 @lgladdy
3 years ago

Replying to juiceboxint:

Yep, the only thing is that you still have to change each user's role to something else and then back in order to update the actual user_level field in the database, if they were assigned that role before you added the level_1 cap. (tested a few minute ago on 4.1) So the workaround itself is pretty obscure, and the caveat for the workaround is even more obscure.

That's what has been fixed and will be released in 4.2 - You won't need to do that anymore - add_cap will update any existing users :)

[Edit: Update typo - 4.2 not 3.2]

Last edited 3 years ago by lgladdy (previous) (diff)

This ticket was mentioned in Slack in #core by lgladdy. View the logs.


3 years ago

#24 @lgladdy
3 years ago

I'm going to write a patch for this one, with the hope of it being included in 4.2

As far as I see it, we should test if they have a level_* greater than 0, or if they have the edit_posts capability.

Is there any other roles that should result in a user being included in the dropdown?

#25 @juiceboxint
3 years ago

Awesome, thanks for tackling it! That would be a good quick fix - any built-in role that has an inherent user level > 1 will also have edit_posts, and authorship has never worked for custom roles prior to your patch (without the arcane workaround anyway) so this seems like a safe bet to me.

A more complete solution (maybe step 2?) would be to consider the post type context as well, though: if the user has the equivalent edit_ cap for a custom post type, they appear in the authors dropdown for those posts. This would allow a user to potentially appear in the authors list for a Post but not a Page, or one CPT but not another, etc.

Again, shouldn't have any BC issues with this because it's never worked before your patch makes it into core. The only thing I wonder about is whether the quick fix will actually create its own BC issues for the more complete fix if that one is done later on. For instance, after your patch, a user with edit_posts but not edit_cpt could be set as the author of a custom post type cpt, and later on when the contextual-author fix is in core, that user is no longer a valid or selectable author of cpt. This will be an extremely unlikely scenario, but it's worth thinking about whether it's worth skipping straight to the complete fix.

#26 @lgladdy
3 years ago

  • Keywords 2nd-opinion added

This is going to be harder than I originally thought.

As earlier in the post, the code at fault here is at https://core.trac.wordpress.org/browser/trunk/src/wp-includes/user.php#L714

If you provide a 'who'=>'authors' key to a get_users command it will return all users with a level greater than 0 using a meta query.

There is no nice way (that I can see) within WP_User_Query::prepare_query for us to do anything smart with capabilities, so i'm thinking there maybe 4 ways to try and fix this.

1) Not use the 'who'=>'authors' key at all, and do a get_users() query to get all the users and then loop through to check their capability. This probably has the potential to be seriously slow on sites with many users?

2) Loop over all the roles, figure out which have edit_posts cap on them and then do a get_users/WP_User_Query query, either using the role option (it currently only supports one role per query, so maybe that could be expanded to support an array of roles? or multiple queries and merge).

3) Add a new capability option into WP_User_Query. This would basically be number 2 anyway - we'd just need to get the wp_user_roles option to figure out the list of roles, and then run that as a query similar to a multiple role search, but someone more familiar with core coding standards, the WP_User_Query class or the Role/Capability component maintainer(s) should be able to say if this is a good idea.

4) Do something inside WP_Roles::add_cap or, more likely, WP_User::update_user_level_from_caps - We could also check if the user has edit_posts and if so, set their default level_* to 1, rather than 0. This seems a bit evil because it's a workaround to legacy/deprecated level_* syntax, and we probably don't want to introduce new code that touches that if the end goal is it not existing at all?

I think 3 actually is a the best idea, depending on how acceptable it would be to use get_option inside WP_User_Query. It would solve the problem, and give developers more functionality.

Thoughts?

#27 @boonebgorges
3 years ago

  • Keywords needs-patch needs-unit-tests added; 2nd-opinion removed
  • Milestone changed from Awaiting Review to Future Release

As scribu said earlier, this is a mess. @lgladdy, thanks for digging into it.

Something like 2 or 3 is probably worth exploring, at least for the short term. As you suggest, the only capability-related data we can easily query on a per-user basis is the deprecated level (which is the original problem here in the ticket) and wpX_capabilities, where wpX_ is the current blog prefix. The latter piece of usermeta stores a serialized array of a user's roles for that blog, and the 'role' param of WP_User_Query translates to a LIKE query against this data. Doing this multiple times is going to scale pretty poorly, but it's not a great deal worse performance-wise than what's already there, and it will allow us to fix the various role-related bugs raised in this ticket.

This ticket was mentioned in Slack in #core by boone. View the logs.


3 years ago

#29 @ocean90
2 years ago

  • Description modified (diff)

#30 @ocean90
2 years ago

#24985 was marked as a duplicate.

#31 @archon810
21 months ago

Any updates here?

#32 @hiddenpearls
21 months ago

Any update on this bug ?

#33 follow-up: @xfechx
18 months ago

I am looking to edit the author of the post, but I can't see any authors (added via members plugin) in the author dropdown.

Has there been any progress on this bug?

Kind regards,

#34 in reply to: ↑ 33 ; follow-up: @juiceboxint
18 months ago

Replying to xfechx:

I am looking to edit the author of the post, but I can't see any authors (added via members plugin) in the author dropdown.

The solution is to add a custom capability called 'level_1' to your custom role. Then users with that role will show up as authors. It won't show up in the capability list once it's been added, but it's there behind the scenes.

Last edited 18 months ago by juiceboxint (previous) (diff)

#35 in reply to: ↑ 34 @Gioni
18 months ago

Replying to juiceboxint:

Replying to xfechx:

I am looking to edit the author of the post, but I can't see any authors (added via members plugin) in the author dropdown.

The solution is to add a custom capability called 'level_1' to your custom role. Then users with that role will show up as authors. It won't show up in the capability list once it's been added, but it's there behind the scenes.

Thanks. That is not a solution for this bug. This is just a workaround.

#36 @juiceboxint
18 months ago

Right, I should have used a different term. The bug itself is still definitely open. I have just given up on waiting for a solution and have incorporated the workaround into my routine. I would encourage you to do the same :)

#37 @master412160
17 months ago

Will this be fixed in future wp version?

#38 @BandonRandon
17 months ago

Hi @master412160 this, like every bug in WordPress, is dependent on a community contribution for a fix. As the bug is open and set to be added to a Future Release this means that once a patch is submitted and verified to fix the issue without having any side effects the project leads will be able to merge into Core WordPress.

If this is a bug that's particularly important to you, I'd suggest writing a patch, or if coding isn't your style, hiring a WordPress developer to take a look at the issue and contribute a patch.

For now, you may use the workaround described in this ticket.

#39 @lukecavanagh
17 months ago

Seems like going with option three from comment 26 @lgladdy

Would be the way to go.

#40 @swissspidy
9 months ago

  • Owner set to swissspidy
  • Status changed from new to assigned

Just stumbled upon this whole user level handling now. Fun. I'll try to find some time to look into it properly.

Even just using a role query in post_author_meta_box() would already be a huge improvement, but I can see the benefits of capability queries.

By the way, for anyone using the Members plugin, add_filter( 'members_remove_old_levels', '__return_false' ); should help there.

#41 @DorZki
3 months ago

Hi all,
Is there anything new about this? this is a serious issue for some of the plugin developers and developers who use WordPress as a framework...

#42 @DekiGk
3 months ago

Is there any chance that we can resolve this?

#43 follow-up: @swissspidy
3 months ago

@DekiGk @DorZki my last comment still stands. I hope to have some time for this soon. If someone else wants to work on this as well, that would be awesome too.

#44 in reply to: ↑ 43 @DekiGk
3 months ago

Replying to swissspidy:

@DekiGk @DorZki my last comment still stands. I hope to have some time for this soon. If someone else wants to work on this as well, that would be awesome too.

Thank you very much for your quick response. Unfortunately, I am not yet confident enough in committing to core. It is, however, always on my mind, and it is something I will try and prepare myself for.

#45 @mcgoode
2 months ago

I have been looking into this and it is a rather deep issue since capabilities can be given to the user directly or to a role.

Roles and their capabilities live in the table wp_options.

select * from wp_options where option_name = wp_user_roles;

User capabilities and roles live in the table wp_usermeta.

select * from wp_usermeta where meta_key = wp_capabilties;

You are able to get users that have capability directly by:

$users = get_users(['role'=>'edit_others_posts']);

Which generates the following SQL

SELECT SQL_CALC_FOUND_ROWS wp_users.* FROM wp_users INNER JOIN wp_usermeta ON ( wp_users.ID = wp_usermeta.user_id ) WHERE 1=1 AND (
  (
    ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"edit\\_others\\_posts\"%' )
  )
) ORDER BY user_login ASC;

This works since its stored in the wp_capabilities array of the usermeta.

public 'allcaps' => 
        array (size=4)
          'read'              => boolean true
          'level_0'           => boolean true
          'subscriber'        => boolean true
          'edit_others_posts' => boolean true

But this does not check the roles assigned and their capabilities. Which is where I am guessing the improvement needs to be made. WP_User_Query needs to have additional functionality added.

As @lgladdy said:

3) Add a new capability option into WP_User_Query. This would basically be number 2 anyway - we'd just need to get the wp_user_roles option to figure out the list of roles, and then run that as a query similar to a multiple role search, but someone more familiar with core coding standards, the WP_User_Query class or the Role/Capability component maintainer(s) should be able to say if this is a good idea.

After looking at WP_User_Query and WP_Meta_Query, which is a helper to create joins, I am not sure you could implement a class like WP_Meta_Query to get the information from the database since you cannot effectively create a join on the two tables needed since both values are serialized.

So querying this from the database cannot be done effectively with how the data is stored currently. So there are two options change how Role and Capabilities are stored and mapped to users or do some PHP wizardry to make this work in the code.

I more for how the data is stored, since I am against storing any serialized code in a relational database, since that breaks the purpose of it being relational. However, I am not knowledgeable on the WordPress core so that might break a lot of thing already in place.

Maybe updating the get_users() function with a if switch that looks for a key in the args array like 'has_capabilities' that then gets all the users and their capabilities? Like someone else already said, sites with large numbers of users this would be an issue for.

Anyway this is something that effected a project I was working on a while back and just now decided to come take a second look. I want to help out, but I am still learning what makes WordPress tick.

#46 @swissspidy
2 months ago

@mcgoode

But this does not check the roles assigned and their capabilities. Which is where I am guessing the improvement needs to be made.

We don't need to check the roles. The dropdown could just show all users with the edit_posts capability. Since the caps are stored in user meta, this should work.

As mentioned before, I'll try to look into this as soon as possible.

#47 @swissspidy
5 weeks ago

  • Milestone changed from Future Release to 5.0

#48 @Brian_Milnes
5 weeks ago

This is still an issue. (Just encountered it when developing CPTs with Pods plugin.)
Must be tricky based on 7 years on, and not fettled, yet! :-o

Last edited 5 weeks ago by Brian_Milnes (previous) (diff)

#49 @swissspidy
5 weeks ago

I started a pull request for this one over at https://github.com/xwp/wordpress-develop/pull/286.

@boonebgorges I'd love your insight. Feel free to commit directly to that feature branch. You can ping Weston if you don't have access yet.

Note: See TracTickets for help on using tickets.