Hi,
I had the need on my site to display the name of current highest userpoints-role a user is in. I put this at the bottom of my userpoints_role.module:
<?php
/**
* Public function to return the name of the highest
* role a user has reached.
*
* @param $userpoints
* current points for user
*
* @return
* name of their role
*
*/
function userpoints_user_highest_role($userpoints){
$role_points = _userpoints_load_role_points();
$roles_within = array();
foreach($role_points as $role_point) {
//put all the roles user is within into an array.
if ($userpoints > $role_point['points']){
$roles_within[ $role_point['points'] ] = $role_point['name'];
}
}
echo max($roles_within);
}
Then in your template, use the following to show the name of the current highest userpoints-role of the user.
<?php
global $user;
$thisUserPoints = userpoints_get_current_points($user->uid);
?>
<div class="fields">Level: <?php print userpoints_user_highest_role($thisUserPoints); ?></div>
The patch is attached for review...
Mike
Comments
Comment #1
kbahey commentedThanks for this patch.
I modified it to make it more straightforward, yet flexible.
All you need now is to do this in the theme:
<?php print userpoints_highest_role() ?>No need to lookup the points before doing that, so less code in the theme. Also, the function takes an optional $uid argument, so you can display the role of users other than the one currently logged in. For example, you can display the role in user lists, forum topic lists, and such.
Please wait for 12 hours, then download the tarball and test it.
If it works, please mark this issue as fixed.
Comment #2
mpaler commentedHi Khalid,
Thanks for cleaning this up. I like how you are handling the uid & points internally. Unfortunately there's some errors :(
Drupal is saying this:
I believe is has to do with the fact that asort may not work on multidimensional associative arrays?
I too thought the $role_points array should be sorted, however my research shows ma arrays need to be sorted using usort(). Perhaps we need to go that way?
Anyway, I had to massage your code as follows to get a good output -- however, now it's returning the first role above the user's highest role.
I believe it's skipping (continuing) for each roll the user is in, and then returning the first role the user isn't in. This is why I put all the roles the user is in into an array and used the max() return the one with the higest points value. I thought that may not be the simplest way to do it, but I couldn't figure out a better way.
Comment #3
kbahey commentedTry this:
I create a new array with only the points and name, points being the key of the array.
Then I sort the array by points, and then do the lookup.
See if that one is better.
Comment #4
mpaler commentedHi Khalid,
Sorry. No joy. Still returning the wrong role.
Here's a version does work. It's a combination of your enhancements and my original code:
Comment #5
kbahey commentedWell, my code has an "off by one" error in it.
For your version, I am still concerned whether you just lucked out and it worked in your case by coincidence.
Two things concern me:
1. The _userpoints_load_role_points() function does not guarantee that the returned array is sorted by points. So, what happens if they are unsorted? We get back the wrong role, and it will be very hard to debug such a case.
An asort() is intended to correct that, but since we have 3 things, I reduced it to two (points and name) with points as the key.
2. Is there a possibility of edge cases where you reach the
returnat the end with nothing in the $roles_within array, and hence the max() returns nothing? What can we do about that?Thanks for your continued perseverance.
Comment #6
mpaler commentedHi Khalid,
"Thanks for your continued perseverance."
No problem. I enjoy doing this!
OK...Let's see if I can answer this...
"Two things concern me:
1. The _userpoints_load_role_points() function does not guarantee that the returned array is sorted by points. So, what happens if they are unsorted? We get back the wrong role, and it will be very hard to debug such a case."
I don't believe the array $role_points needs to be sorted. The array can be totally out of order and the loop will still iterate over the entire array, putting all elements into $roles_within that fit our condition. In our case the condition is if the userpoints of the current user is higher than the value of userpoints threshold for this role.
So now we have all the roles the user is in put into $roles_within as an associative array with points as the key and name as the value. Now if we run the max() function on $roles_within
it will return the value (name) of the element with the largest key (point) value. Again, I believe the key thing here (no pun intended) is that the $roles_within array doesn't need to be sorted...max() will find the largest value for us.
"2. Is there a possibility of edge cases where you reach the return at the end with nothing in the $roles_within array, and hence the max() returns nothing? What can we do about that?"
I believe this function will always return a value as long as the user has a userpoints value that fits within one of our userpoints role thresholds. How do we ensure this? For starters, I believe it would be up to the administrator to be sure they have the roles points set up properly. Beyond that...we must ensure the user's userpoints never goes below the lowest userpoints role. This can be achieved by the following:
1. make the lowest role userpoint value == -1 and use the do not allow negative userpoints contrib module.
2. make the lowest role userpoint value equal to a very low negative number? eg -1000000
What do you think?
Comment #7
kbahey commentedFor #1, I think your code works because it does not break out of the loop and keeps checking to the end of the array. So, my concern is addressed.
For #2, I don't like magic values. They are trouble in the long run. We can find the min() of the array, and initialize with that. What do you think?
Comment #8
mpaler commented"For #2, I don't like magic values. They are trouble in the long run. We can find the min() of the array, and initialize with that. What do you think?"
I will look into it -- could you explain a bit more what you're thinking? Which array?
Thanks,
Mike
Comment #9
kbahey commentedIf you look at the foreach in #6, instead of using -1 or some other "magic value", we can use min() of the $role_points, returned form the _userpoints_load_role_points() function.
This will be the lowest number for roles. We initialize to that, so that if the foreach misses the highest for any reason, we still have a valid number to return.
Am I being clear?
Comment #10
mpaler commentedHi Khalid,
Sorry for the long delay! I've been really busy and am just getting back to this...
How does this look? I believe it eliminates the need for magic values. If it looks good to you, I'll supply a new patch...
Mike
Comment #11
kbahey commentedFixed in 5.x-3.x.
Comment #12
Anonymous (not verified) commentedAutomatically closed -- issue fixed for two weeks with no activity.