We often use points to give a motivation for users to do something that is more important to the community than it is to the user himself. Showing a list of users along with the points give motivation to some people. But they should know for which action they get points and if so how many are to gain.

I imagine a block that you place along this list (or any other list) that shows automatically the current points that are set on userpoints settings form. Each line for an event (useraction) and the points associated to. Of course, you could write this statically. But a dynamic block would guarantee to be always up to date. This is important in any serious application.

Implementing this is not easy, I believe. I see several approaches:

  1. Extracting all userpoints variables that represent userpoints to get or to lose.

    Userpoints for events are stored in variable table. They are called userpoints_modulename_eventname. Unfortunatly, even other information is stored this way and points are stored as strings. There are even variables containing integers representing bools or thresholds. Distinguishing is hard to do automatically, except you assume points being greater than 1 and lower than thresholds. I tried this approach in a local solution, see below. But I came to the conclusion that it's a poor solution that cannot be gereralized.

  2. Searching the code for hooks that implement points, identify userpoints variables from that and select the points from the database.

    Well, I simply don't know how to do this searching hooks. Even if this works we still have the problem that we need a user readable name for each event that you could get points for.

  3. Change the backend: Store points an own table together with the variable name, a user readable name and a description for end users. API has to be changed to set these values.

    The most clean solution, I believe. But API changes are slow changes. Anyway, this would giv opportunity to store more information on points. You could define several kinds of points ("trusties", actionpoints, money, lives, whatsoever...), a realm, or save the name the module that handles this. You could even distinguish points from thresholds.

I attach my approach not as a patch, it is notworth to be generalized. Maybe this is interesting for people that need a fast local solution until the problem is solved generally.

/**
 * Implementation for hook_block
 */
function local_block($op = 'list', $delta = 0) {
  global $user;

  $block = array();
  if ($op == 'list') {
    $block[4]['info'] = t('Userpoints table');
    return $block;
  }
  else if ($op == 'view') {
    switch ($delta) {
      case 4:
        if (module_exists('userpoints')) {
          $block['subject'] = t('Awards');
          $variables = _local_get_userpoint_events();
          $output = theme('item_list', $variables);
          $block['content'] = $output;
        }
        return $block;
    }
  }
}

function _local_get_userpoint_events() {
  $result = db_query("SELECT name, value FROM variable WHERE name LIKE 'userpoints_%';");
  while ($row = db_fetch_object($result)) {
    $unserial = unserialize($row->value);
    if (is_string($unserial) && strlen($unserial) < 3) {
      $value = (int) $unserial;
    } 
    // all points greater than 1, all thresholds lower than 20
    if (isset($value) && $value > 1 && $value < 20) {
      $variables[$row->name] = t($row->name) . ': ' . $value;
    }
    unset($value);
  }
  return $variables;
}

Comments

jredding’s picture

This would require some pretty massive changes to pull off and require the coordination of all the module developers. Even if it were possible to do this I'm not sure if its really worth it. We're talking about saving 5 minutes of time in creating a "how to earn user points page". Especially since admins are, usually, the only people that are creating new points to be added to a site and point "events" are usually kept to a minimum.

I'm just not seeing the value add to this amount of code especially given the fragility of it all and the rather static nature of point events (not to mention the performance hit to display this block dynamically).

Not all dynamic things are good.

However if this were desirable.. a "hack" would be to do this after-the-fact. Using the userpoints hook and the "points_before"/"points_after" you could grab the events throw them into the variables table and create the block (as stated above). Of course this is only after-the-fact and if a particular point event is never granted them it'll never show up.

My suggestion would be to roll the "hack" and post it. If there is support for it then we can start looking at modifying the core module to support this.

Sorry I just don't see the value in this right now.

marcor’s picture

I agree that it is not really necessary to have a points block. But the example shows that the userpoints API could be improved to handle this and many other improvements to the userpoint sytem. Users should not work with drupal variables when collecting points, they should work with a function that hides the actions. If there would be a function like that, it would be easy to exchange the variable solution in favour of a table solution.

Another point is, that there are other userpoints approaches going on, that would benefit from this storing solution ("Point Categories", http://drupal.org/node/151037). All this could easily be done if point values would be stored in a table.

Something like "userpoints_awards" (or "_gains", "_delta", whatsoever... let native speakers choose the name)

CREATE TABLE {userpoints_awards} (
  module       VARCHAR(255) NOT NULL
                DESCRIPTION 'Module name like in systems table',
  variable       VARCHAR(255) NOT NULL
                DESCRIPTION 'technical name for variable without module prefix',
  name          VARCHAR(255) NOT NULL
                DESCRIPTION 'human readable name',
  description   VARCHAR(255) NOT NULL
                DESCRIPTION 'description for end users',
  value         INT(11) NOT NULL DEFAULT '0' 
                DESCRIPTION 'points to gain or to lose for this event'
  max          INT(11) NOT NULL DEFAULT '0'
                DESCRIPTION 'maximum points for this event',
  type          INT(11) NOT NULL DEFAULT '1'
                DESCRIPTION 'points account type'
  PRIMARY KEY (module, variable),
  KEY (type) 

This would give ability to add certain additional features:

  1. Build another userpoints_type table where several kinds of points can be handled differently, like points for "karma", "trust", "money", "kisses"... (issue http://drupal.org/node/151037)
  2. Define max points. If people want to raise their points, they cannot rely on one action. They have to do different actions. So userpoints could evolve from a value of effort to a value describing experience. (no issue, yet)
  3. And show an awards-block, of course.

What do you think?

jredding’s picture

You asked me what I thought and unfortunately my thoughts are that you haven't really looked into this solution.
The biggest example you pointed, "Point Categories", was my thread and one that HAS been implemented. We hashed it out and decided that using a solution that you propose wasn't the best solution thus we opted for using taxonomy to branch out categories.

This knocks out #1

#2 can be handled with probably 10 lines of code in a custom module. (use the userpoints hook on points_before.. decline points, set a message).

#3 just doesn't seem like a necessity.

The changes you are suggesting would require massive API changes but more importantly would shift more responsibility to the developers. As it stands currently a developer only needs to do userpoints_userpointsapi(5); which would grant 5 points to the currently logged in user. This could be done anywhere in Drupal leaving for an immense amount of flexibility.

Your suggestion would require developers to create a more robust module and register their events before they can be used. While I agree that it is somewhat "cleaner" of a solution I just don't think there are benefits to this. I find this analogous to Drupal's node table. There are much cleaner way of handling this table but the current method provides an immense amount of flexibility.

However if you really think this is way that userpoints should go if can be done now. Its a hack but it can be done.

1) Create the table as you suggest.
2) use the hook_userpoints "points before" to filter out any non-registered events.
3) set the entity_id to the PK on your suggested table, set the entity_type to "userpoints_awards"

Done!. I'm serious. You can roll this as a contrib module and see if there is real support for it. You can do this now w/o any API changes.

Try not to take this personally as its just a matter of code but I just don't see the value-add in this.

marcor’s picture

Oops sure that was YOUR Thread!!! State is still active, that made me think it's still undone. I'm sorry for that!

Thanks for your detailed explanation! Probably your solution is not the way I "thought" (I searched and searched for hooks I expected to be somewhere), but reading twice I begin to understand the ease of the concept. Maybe that was the shortest and best description of userpoints usage on drupal.org I read.

I have to think about that because I need a solution that embraces the retro module, too. Hooks would be very important here, that made me suggest a more general approach. Leaving this open.

jredding’s picture

Version: 5.x-2.14 » 5.x-3.x-dev
Status: Active » Closed (works as designed)

Personally I think you need to sit down and really look at the module and the hooks it currently offers. The readme.txt file in the root of the module directory will help you out.

You're right documentation is shaky at best and could be improved but we're all volunteers here so once you get an understanding of whats going on with this module considering becoming one of those volunteers (i.e. help us improve the docs).

I'm changing the status to "By design" because currently it is not designed to do what you initially proposed it to do. If, after you've taken a hard look at the code, think that the API needs to be changed then come back, post a patch or a commanding argument and change this back to "active".

Finally make sure that you look at Version 3.x which, although still in BETA, in nearly stable. There is a remaining bug with userpoints_basic.module which is preventing the release.

Thanks for the healthy debate its people like you that take the time to comment and think about design that help to push OSS further.

jredding’s picture

You're right the status of the previous thread wasn't changed correctly. That was my/our fault. remember we're all volunteers here. ;)