Is there a way to list only the users that has a specific capability, such us "publish_posts" ?
To select users with certain capabilities you can use WP_User_Query with meta_query parameter, because WP stores capabilities as a serialized string in user_meta table.
Also remember that due to availability to have multisite installation capabilities name in user meta looks like wp_table_prefix_capabilities.
global $wpdb;
// meta-key name
$capabilities_field_name=$wpdb->prefix.'capabilities';
//array as argument for our query
$qargs=[
'role' => ['Customer'], // use this if you need to query by role at the same time
'meta_query'=>
[
'relation' => 'OR', // optional if you'll need to select more than
// one capability just add this and create same array
// as down below describing what are you looking for
[
'key' => $capabilities_field_name,
'value' => 'your_role_name',
'compare' => 'LIKE',
],
// here could be same array [key,value,compare]... as above with another capability
// but you'll need to add extra argument showing relationship between them see above 'relation parameter'
],
'number'=> -1 // to select all users
];
$usersQuery=new WP_User_Query($qargs); // instantiate UserQuery with $qargs
$users=$usersQuery->get_results(); // get all results as array of WPUser objects
Hope it helps somebody:)
Note [vars] could be substituted to array(vars), I like [] short syntax but it's supported only since php 5.4.
You can just retrieve all users. Then loop through them in a foreach. Check if the user has a specific capability then push the users to another array and use that array to list them.
$all_users = get_users();
$specific_users = array();
foreach($all_users as $user){
if($user->has_cap('specific_capability')){
$specific_users[] = $user;
}
}
NOTE:
It seemed a nice quick and dirty solution at the time, but now I would recommend writing a query. I do not have the time to investigate this for you, so if the one downvoting this would be so kind to answer this question instead of downvoting an answer which was an actual help to the inquirer, that would be nice.
You can list users with WP_User_Query, but afaik you can only return different roles, not permissions, maybe that's already what you want! There's also a site where you can see the different roles in the wordpress documentation.
You will first need to get all the roles that contain that capability. Then you can search users based on the roles that contain that capability.
$roles = array();
foreach ( wp_roles()->roles as $role_name => $role_obj ) {
if ( ! empty( $role_obj['capabilities']['my_capability_name'] ) ) {
$roles[] = $role_name;
}
}
$users = get_users( array( 'role__in' => $roles ) );
This does not account for if another role has "Deny" on that capability and your users can contain multiple roles. If so then you will also need to add a "user_can()" condition when looping through your Users. https://developer.wordpress.org/reference/functions/user_can/
Related
I'm trying to setup a bbpress with extended user capabilities.
The problem
My goal is that users need to have different capabilities in each forum, i.e:
UserA can't access ForumW
UserA can only read topics and replies in ForumX
UserA can create topics and write replies in ForumY
UserA can moderate ForumZ
Plugins
These are the plugins I tried so far, but without success:
Ultimate Member, official 1.7 and the new 2.0 version
https://ultimatemember.com/
They claim that they're working on a groups extension for UltimateMember v2, which somehow looks promising, but as of now there's no release date and I still don't know if this extension is going to solve my problem.
itthinx Groups plugin
http://docs.itthinx.com/document/groups/
Allows me to assign multiple groups to users and forums, but there's still a catch.
First attempt
Since itthinx Groups plugin allows me to assign multiple groups to UserA, which is great, it's still not solving my issue.
So, I tried something like this:
ForumX has the following groups assigned: ForumX_readers, ForumX_writers, ForumX_moderators
UserA has the following groups assigned: ForumX_readers, ForumY_writers, ForumZ_moderators
But the problem is, since UserA belongs to groups that have publish_replies and moderate capabilities, he has full access to ForumX.
So what I need is an intersection of the forum-groups and the user-groups - which in this example is ForumX_readers.
The promising part, but...
I digged into the code of the plugin and found the line that handles the capabilities of the user based on his assigned groups and quickly tried to get the current forum groups, to implement the intersection.
Unfortunatelly I was not able to access the global $post, the $_GLOBALS['post'] nor the $_REQUEST[] variables in this part of code. Neither directly nor with an apply_filters() function, that I implemented into the part of the code myself.
UPDATE:
I was able to get the ID with get_posts() and the slug of the current forum/topic.
So, my question
Is there any solution to my first attempt, which I may have overseen?
If not, is there maybe any other plugin that can solve my problem that I'm not aware of?
Or is something like that even impossible in bbpress?
After some further research and trial & error, I finally figured it out.
First step to do is to set up the capabilities, which in my case look something like this.
In the plugins directory, there is the file core/class-groups-user.php. The init_cache() function retrieves the assigned groups to the user, and sets the according capabilities.
To not mess around to much with the core-plugin, I applied a filter to the $group_ids variable which can be found in line: 415.
foreach( $user_groups as $user_group ) {
$group_ids[] = Groups_Utility::id( $user_group->group_id );
}
// added this line
$group_ids = apply_filters('filter_user_group_ids', $group_ids);`
I then created a new plugin, which hooks into this filter.
add_filter('filter_user_group_ids', 'dnmc_filter_groups', 10, 1);
function dnmc_filter_groups($user_group_ids) {
$forum_id = dnmc_get_forum_id();
if(!$forum_id) return $user_group_ids;
$forum_group_ids = Groups_Post_Access::get_read_group_ids( $forum_id);
$user_restricted_forum_group_ids = array_intersect($user_group_ids, $forum_group_ids);
return $user_restricted_forum_group_ids;
}
function dnmc_get_forum_id() {
$args_topic = array(
'name' => basename( untrailingslashit( rtrim($_SERVER['REQUEST_URI'], '/') ) ),
'post_type' => 'topic',
'post_status' => 'publish',
'numberposts' => 1
);
if($topic = get_posts($args_topic)) {
return $topic[0]->post_parent;
}
$args_forum = array(
'name' => basename( untrailingslashit( rtrim($_SERVER['REQUEST_URI'], '/') ) ),
'post_type' => 'forum',
'post_status' => 'publish',
'numberposts' => 1
);
if($forum = get_posts($args_forum)) {
return $forum[0]->ID;
}
return false;
}
I want to perform bulk update of users with a Approved users, the table
field_user_status_value
-----------------------
entity_type, entity_id, field_user_status_value
The entity_id is the user id which does not exist in the table, below is the custom module I wrote to update the table:
function bulkapprove_action_info() {
return array(
'bulkapprove_action_callback_name' => array(
'type' => 'user', // Can be file, term, user, etc.
'label' => t('Approve User'),
'configurable' => FALSE, // Doesn't need config form
'behavior' => array('view_property'), // Uses view access rights ,
'pass rows' => TRUE,
'triggers' => array('any'), // Works always
),
);
}
function bulkapprove_action_callback_name($entity, $context)
{
db_update('field_data_field_user_status')->fields(array('field_user_status_value' => 'Approved'))->condition('entity_id', $context->entity_id)->execute();
}
But it is not inserting the values in this table
In Drupal you do not want to update the database fields directly unless you created the table. Drupal's internal APIs provide a collection of tools to ensure you update the values correctly and that all supporting modules get notified of changes as needed through the hook system.
In this case the callback gets the actual entity to run your action against (in this case the user object). You want to take action on that entity and then save the entity.
function bulkapprove_action_callback_name($entity, $context)
{
$entity->status = 1;
entity_save('user', $entity);
}
I created the following token; however, when I try to use site:coupons as a data selector in a loop action
It does not appear in data selection browser. Note that it does appear as replacement pattern when i use for example "Show a message on the site" action.
I spent lot of time searching in the internet and rules' token' issue queue, i tried to read the source codes of core token , token and rules as well. I also found some information too like data selector are no tokens! or rules only works with entities!
So far i couldn't get this to work no matter hard i tried. My data is not entity. Is there anyway to integrate it with rules?
I couldn't find any official documentation on this so i created an issue with hope that some of the rule's experts can help me out.
Note : if i replace site with coupon-link in the following code, it won't even appear as replacement pattern in rules. but it works fine as token anywhere else but in rules
Thanks in advance
<?php
/**
* Implements hook_token_info().
*/
function coupon_link_token_info() {
$types['coupon-link'] = array(
'name' => t("Coupon link coupon info"),
'description' => t("Info about linked coupon via url."),
);
// Andy Pangus specific tokens.
$tokens['site']['coupon-code'] = array(
'name' => t("Coupon Link Coupon Code"),
'description' => t("The code of the coupon entered via url."),
);
$tokens['site']['coupon'] = array(
'name' => t("Coupon Link Coupon"),
'description' => t("The coupon entered via url."),
'type' => 'commerce_coupon'
);
$tokens['site']['coupons'] = array(
'name' => t("Coupon Link List Coupons"),
'description' => t("The coupons entered via url."),
'type' => 'array'
);
return array(
'types' => $types,
'tokens' => $tokens,
);
}
/**
* Implements hook_tokens().
*
* #ingroup token_example
*/
function coupon_link_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
$sanitize = !empty($options['sanitize']);
// Text format tokens.
if ($type == 'site' && __coupon_link_get_coupon_code()) {
//$format = $data['format'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'coupon-code':
// Since {filter_format}.format is an integer and not user-entered
// text, it does not need to ever be sanitized.
$replacements[$original] = $sanitize ? filter_xss(__coupon_link_get_coupon_code()) : __coupon_link_get_coupon_code();
break;
case 'coupon':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = __coupon_link_get_coupon(__coupon_link_get_coupon_code());
break;
case 'coupons':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = array(__coupon_link_get_coupon(__coupon_link_get_coupon_code()));
break;
}
}
}
return $replacements;
}
?>
A few things.
Tokens are formatted as [type:token] as explained on the hook_token_info api page. For your example, it would be [coupon-link:coupon]. I'm not sure why you're appending your tokens to the site array, as your custom coupon token probably has nothing to do with sitewide tokens like *site_url* or *site_name*.
Because the types are machine names, you should change it to coupon_link as machine names with dashes are not Drupal standard.
If you truly get lost, I suggest also looking at the token example from the examples module.
In the context of organic groups, I am writing a module which will stop users who are not members of a group from adding group posts into that group.
My module currently sets the permissions necessary and detects whether a user has the permission.
So when a user(s) are looking at a group page, I want to disable/remove the standard link to create group posts.
Try this method.
function mymodule_menu_alter(&$items) {
global $user;
// Perform code for finding out users permissions.
// lets suppose we set true or false to $restricted after all
if ($restricted && isset($items['node/add/yourtype'])) {
$items['node/add/yourtype']['access arguments'] = FALSE;
// or unset($items['node/add/yourtype']) to remove item for user
}
}
If I understood right you don't want certain users to create a content type.
So the steps are:
1) Create a menu hook.
// Here we make sure if the user goes to for creating this node type
// we can use the appropriate call back function to stop it.
function yourmodoule_menu() {
$items = array();
$items['node/add/page'] = array(
'page arguments' => array('yourmodule_additional_actions'),
'access arguments' => array('administer create content')
);
}
2) Then make a permission hook to make sure only certain users have this permission.
// drupal will only allow access to to path 'node/add/page' with people
// who have access given by you.
function yourmodule_permission() {
return array(
'add content' => array(
'title' => t('Administer create conent'),
'description' => t('Perform administration tasks and create content')
)
)
}
3) Write your code for those users who have the permission.
// Only affter they have this permisson drupal will allow them access
// to the below function.
function yourmodule_additional_actions() {
// this code will only execute if the user has the permission
// "Administer create conent"
}
I'm using WordPress to make my users make their own website/blog. I have a set up that I'm cloning out to all the users with some special user-roles and standard plugins.
However, some of the plugins are not supposed to be changed or inactivated by the users.
Is their any way to select which plugins different user roles are allowed to use? Or a easy way to hide some plugins in the plugins-page but still have them working as normal?
Maybe there's some plugin that helps me to do this?
You could write a plugin that uses the "all_plugins" filter hook to remove from the array plugins that you don't want displaying for a certain user. Something like this:
$plugin_credentials = array(
'bob' => array(
'Hello Dolly' => 1
),
'jim' => array(
'Akismet' => 1,
'Hello Dolly' => 1,
),
'admin' => "**ALL**"
);
function plugin_permissions($plugins)
{
global $current_user, $plugin_credentials;
$username = $current_user->user_login;
if ($plugin_credentials[$username] == "**ALL**")
return $plugins;
$viewable_plugins = array();
foreach ($plugins as $plugin) {
if (isset($plugin_credentials[$username]) &&
isset($plugin_credentials[$username][$plugin['Name']]) &&
$plugin_credentials[$username][$plugin['Name']] == 1) {
array_push($viewable_plugins, $plugin);
}
}
return $viewable_plugins;
}
add_filter('all_plugins', 'plugin_permissions');
Managing the user permissions in the plugin itself is not ideal, but it is probably easiest. You can expand on that idea to create admin pages for managing the users and their viewable plugins in a database table somewhere.
Each plugin will usually specify their own role/permission, which you can see if you look at their add_submenu_page() or such function calls. You can create new roles for those plugins and replace the one specified by the author, but it will also break the changes if you upgrade the plugins.
You should stratify the users. Make sure that the Admin user(s) are trusted and know not to fiddle with what they don't understand. The others should be limited to their roles. Authors, editors, etc. For example, if they're just a part of the site to write articles, then they don't need to see the rest of it. Make them an author and be done with it.
This is part of client education. If its a smaller client with less stratified roles, then make them two accounts. Tell them "this is the account you administer the site with, you'll be using this rarely. And this is the account that you'll use most of the time to write and edit. You can do all of your daily tasks here and will most likely never need the administrator account". You won't always have luck with this approach, but its less time and effort invested in crap you shouldn't be wasting time on.
I've done a new version based on #spuriousdata Answer. This one uses the plugin slugs (file name minus the extension) to build the list of restrictions. This way is easier as we can unset the array using the first level $keys.
Configuration instructions in the code itself.
<?php
/**
* Plugin Name: Limit Plugins by User
* Plugin URI: http://stackoverflow.com/q/14340131/1287812
* Description: Show selected plugins for specific users.
* Based on the code by spuriousdata, http://stackoverflow.com/a/3713985.
* Author: brasofilo
* Author URI: http://wordpress.stackexchange.com/users/12615/brasofilo
* Version: 1.0
* License: GPLv2 or later
*/
add_filter( 'all_plugins', 'plugin_permissions_so_3707134' );
/**
* Filter the list of plugins according to user_login
*
* Usage: configure the variable $plugin_credentials, which holds a list of users and their plugins.
* To give full access, put a simple string "ALL"
* To grant only for some plugins, create an array with the Plugin Slug,
* which is the file name without extension (akismet.php, hello.php)
*
* #return array List of plugins
*/
function plugin_permissions_so_3707134( $plugins )
{
// Config
$plugin_credentials = array(
'admin' => "ALL",
'other-admin' => array(
'akismet',
),
'another-admin' => array(
'akismet',
'hello',
),
);
// Current user
global $current_user;
$username = $current_user->user_login;
// Super admin, return everything
if ( "ALL" == $plugin_credentials[ $username ] )
return $plugins;
// Filter the plugins of the user
foreach ( $plugins as $key => $value )
{
// Get the file name minus extension
$plugin_slug = basename( $key, '.php' );
// If not in the list of allowed plugins, remove from array
if( !in_array( $plugin_slug, $plugin_credentials[ $username ] ) )
unset( $plugins[ $key ] );
}
return $plugins;
}