bbpress user-roles or capabilities per forum - wordpress

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;
}

Related

Bulk restore revision on all posts on wordpress

Is there a wordpress plugin to restore revisions on multiple posts at once based on date or position?
I have a hacked site in which every post was changed. To make all content clean I would need to restore all posts to a wherever revision was online 5 or 6 days ago.
Thanks in advance!
Honestly, with a hacked site - your best bet is to restore a Database/MySQL backup from a time when it was "clean", or just reverting them manually.
That said, WordPress does let you retrieve revisions using wp_get_post_revisions().
NOTE: This is the plural function, the singular function wp_get_post_revision() get a revision by its ID. Unless you know what the ID of the revision you want is, you don't want this function.The plural function gets all revisions of a post.
Now, before you continue reading - BACK UP YOUR SITE.
Did you back it up?
Make sure you HAVE A BACKUP.
Also, make sure it's a backup that works.
Now, that you have a backup...
Take a look at this function that I came up with, it uses wp_get_post_revisions() in conjunction with wp_update_post(). Again, PLEASE make sure you back up before running this function. It's going to mess with posts and post content, and if it stalls/dies, it could get messy. Use at your own risk.
First, it makes sure you WANT to run this function by visiting https://yoursite.com/?restore_revisions=so_50959583_restore_revisions. It needs that query string to run.
Then it makes sure the current user has admin level privileges.
Then it checks to see if this function has run before, so it doesn't run every time.
Now it runs a standard WP_Query to grab all posts. Note I've ommitted other post types, you can add those in if you want.
Inside the loop, it checks to see if a revision exists, and if so it uses wp_update_post to update the post to the revised post_content.
If it fails here, it will die with a message to prevent setting the flag we checked in the beginning.
Provided it runs smoothly, it sets the so_50959583_restore_revisions_flag option flag so it won't ever run again.
At this point, you should remove the code.
NOTE you could remove the option flag, and set this up as a custom plugin and run the function off of register_activation_hook.
So, here's the beast, best used for illustrative purposes, use it at your own risk.
add_action( 'init', 'so_50959583_restore_revisions' );
function so_50959583_restore_revisions(){
if( isset( $_GET['restore_revisions'] ) && $_GET['restore_revisions'] == 'so_50959583_restore_revisions' ){
if( current_user_can( 'manage_options' ) ){
if( !get_option( 'so_50959583_restore_revisions_flag' ) ){
// This hasn't run before, run it now.
$args = array(
'post_type' => 'post', // Limit to just posts
'posts_per_page' => -1
);
$revision_query = new WP_Query( $args );
if( $revision_query->have_posts() ){
while( $revision_query->have_posts() ){
$revision_query->the_post();
if( $last_revision = array_shift( wp_get_post_revisions( $post->ID ) ) ){
// At least one revision existed.
$reverted_post = array(
'ID' => get_the_ID(), // Update current post in query
'post_content' => $last_revision->post_content // set content to previous content
);
// Update the existing post
if( !wp_update_post( $reverted_post ) ){
wp_die( 'Something went wrong' );
}
}
}
wp_reset_postdata();
}
// Whew, completed!
update_option( 'so_50959583_restore_revisions_flag', true );
}
}
}
}
I would like to stress, again, that your best bet is to keep backups of your database using one of the many available (and ofttimes FREE) plugins, to prevent issues like this from occurring.
Last, if you only have like 20-30 posts, you should just do it by hand. And if you have like 10,000 posts you may need to have this code do it in "chunks" or it may timeout.
Best of luck

Woocommerce Dynamic Pricing - manually inserting pricing rules

I am using the Woocommerce Dynamic Pricing plugin, which is very useful but does not easily allow me to ammend pricing updates via. the database.
Looking at the sample data,
{"set_5339c459a78e7":
{"conditions_type":"all",
"conditions":{"1":{"type":"apply_to","args":{"applies_to":"everyone"}}},
"collector":{"type":"product"},
"mode":"block",
"date_from":"",
"date_to":"",
"rules":{"1":{"from":"","to":"","type":"price_discount","amount":""}},
"blockrules": {"1":{
"from":"2",
"adjust":"1",
"type":"fixed_adjustment",
"amount":"0.26","repeating":"yes"}}}}
it seems that the meta requires set_* to be specific with the product meta, or it will not apply correctly.
Revising the code, I notice this:
$terms = get_terms('product_cat', array('hierarchical' => false, 'hide_empty' => false, 'parent' => 0));
foreach ($terms as $item_id => $item) {
$set_index = $item->term_id;
$name = 'set_' . $set_index;
}
This is bizarre to me, as the term_id appears to be a 13 character alphanumeric string than the expected bigint. Could anyone explain how I can reproduce this string for when I manually update my tables?
I had a look through the code and I found that in /admin/admin-init.php where it specifies the AJAX handlers, this is how it comes up with new set names:
function woocommerce_pricing_product_admin_create_empty_ruleset() {
global $wc_product_pricing_admin;
$wc_product_pricing_admin->create_empty_ruleset( uniqid('set_') );
die();
}
As you can see it just uses PHP's uniqid function prefixed by nothing but "set_" so in the case of product rules it appears to be just a random ID.
Hope that helps.

WordPress function get_the_terms() returns 'Invalid taxonomy' error [duplicate]

On my local machine it works fine, but on the live server, when I run
get_terms('taxonomy' => 'any_taxonomy');
it returns a list of terms, but if I add any parameter to it like:
get_terms('taxonomy' => 'any_taxonomy','hide_empty' => false);
Then it returns "invalid taxonomy". The issue is not that the taxonomy is not registerd get_taxonomies() before it, it shows that all the taxonomies are registered. If I do this:
get_terms('taxonomy' => 'category','hide_empty' => false);
The same problem is there, so its not just custom taxonomies.
Any idea what could be causing this issue?
Can anyone suggest a way to diagnose this?
i'd say you are running different versions of wordpress, 4.5 changed the functionality of get_terms()
get_terms( array('taxonomy' => 'any_taxonomy','hide_empty' => false) );
Should work, but i also think if you are calling get_terms() before registering your taxonomy is going to cause issues.

How WordPress can list users with specific capabilities

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/

Multiple concurrent database connections in drupal 7

I'm writing a wrapper class for my drupal 7 site which lets me connect to and query my phpbb database.
When connecting to an external data source (as per drupal documentation) you have set the active db, run the query, then set the active db back to the default.
e.g.
db_set_active('phpbb');
$result = db_query($sql,$args,$opts);
db_set_active();//back to default
But is there any way to use drupal's database wrapper to create a brand new connection which can be permanently set to the new database without having to do this switching back-and-forth nonsense? surely we can handle connections to multiple databases concurrently.
I have done some googling but haven't found anybody trying to do this as yet.
Typical. 5 minutes after posting i figure it out... so, for future googlers:
Basically, you don't use db_query, instead you run the query on your connection without setting the active link.
you can figure this out by looking at how db_query works:
http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_query/7
So it looks like this:
$target='default';
$key = 'phpbb';
$phpbb = Database::getConnection($target,$key);
$result = $phpbb->query($sql,$args,$opts);
This assumes you have a database configured in your settings.php like the following:
$databases['phpbb']['default'] = array(
'driver' => 'mysql',
'database' => 'forum',
'username' => 'username',
'password' => 'password',
'host' => 'mysql.host.com',
'prefix' => 'phpbb3_'
);
Database::addConnectionInfo() perhaps?
This method allows the addition of new connection credentials at
runtime. Under normal circumstances the preferred way to specify
database credentials is via settings.php. However, this method allows
them to be added at arbitrary times, such as during unit tests, when
connecting to admin-defined third party databases, etc.
If the given key/target pair already exists, this method will be
ignored.
The definition for getConnection cites a different order for arguments than used above.
function getConnection($target = 'default', $key = NULL)
This is sadly different from Database::addConnectionInfo() which is
public static function addConnectionInfo($key, $target, $info)
Also, on DB_select, the $key is not a parameter, though it is in the options array:
function db_select($table, $alias = NULL, array $options = array()) {
if (empty($options['target'])) {
$options['target'] = 'default';
}
return Database::getConnection($options['target'])->select($table, $alias, $options);
}
while
final public static function getConnection($target = 'default', $key = NULL) {
so this implies that the 'master' or 'slave' or 'default' is always used as set, but not the key to the alternative database/schema, requiring the db_set_active('...'); and db_set_active(); around the db_select.
Since calls to other dbs can easily be required within the processing of the db_select (such as devel calls or calls in alters), this is inflexible design. Changing this call:
return Database::getConnection($options['target'])->select($table, $alias, $options);
to add the Key parameter (it is already spec'd as an argument!!) is needed but insufficient so far as I can now see.

Resources