Essentially, if all goes as planned, we would be able to drop the "User" from "Userpoints" ;)

Important: We absolutely want to keep it as easy to install as it is now. Meaning, when you install the module, it will configure itself to work more or less like you know it out of the box.

Comments

BenK’s picture

Subscibing... definitely agree with your "important" point.

guybrush’s picture

The problem with trying to do this is that the user is referenced so many times within the module. One option might be to make a change so that instead of the UserpointsTransaction class extending the Entity class directly, it instead extends a EntityTransaction class. The only difference is that the EntityTransaction class would not be interested in the user's uid.

It may be possible to continue to use the existing userpoints_txn table structure, but make the uid field nullable.

Entity points transactions could then be attached to entities by doing something like attaching an entity reference field, or similar to it. That would introduce a dependency on either the entity_reference or relation module, for example.

guybrush’s picture

Other options that occur to me are:
1. Rename the uid field to something like entity_id, and add a field to specify the entity type (defaulting, perhaps, to 'user')
2. Make the standard implementation a generic Entity Transaction, but then have a feature module for 'user', which brings in the standard userpoints functionality using Entity Reference. Example feature modules could be added for alternative entity points transactions (e.g. attached to a Basic Page node)

berdir’s picture

Yeah, I never said that this is going to be easy. :)

I'm not sure if having generic EntityPointsTransaction class helps because SQL doesn't support something like that and we'd need multiple tables.

So yes, we will need to change uid in the transaction to something like entity_type/id. The thing is that this already exists, so we either need a new name or rename the old one. Entity Reference doesn't work, because that's limited to a single entity type per field. Relation technically would, but then we have yet another entity and table inbetween.

Additionally, this is already related to the "replace tid with bundles" issues, because we then have two different ways of separating transactions: a) multiple transaction bundles, and b) multiple fields to which they are assigned.

So, my suggestion at this point is the following, let's split this issue up into two separate tasks:

1. Use a field to store the points total.
2. (Later on) Make it possible to assign such a field to another entity than user.

The first point has already enough conflict potentation, especially with #1258026: Implement userpoint bundles. We might want to split that one up as well and first remove the current categorization (and replace with a taxonomy term reference field if it's currently used), then implement 1. here and then look into making it entity type agnostic and add bundles.

The advantage of that is that we currently have two tables to store userpoints totals, one is the overall total and one is per category. The per-category one would be removed, which would make it easier to replace the other one with a field.

mradcliffe’s picture

This is going to require splitting off the user field and functionality attached to user accounts into a separate sub-module so that we can create the field type in userpoints and create the field and field instance in the sub-module.

I'm working on a patch for this.

mradcliffe’s picture

Status: Active » Needs review
StatusFileSize
new8.8 KB

Here's a patch that leaves in the current stuff, but adds an optional field that can be added with the userpoints_user sub-module. Todo: tests, upgrade, and removing of old implementation.

The only other way is to do like Og does and create an interface to "add fields" to the entity after installation. I'm not sure which is better UX.

I guess another alternative is to make userpoints_field and then have userpoints depend on that sub-module instead. Then userpoints can be the one adding fields that userpoints_field defines the field types for.

berdir’s picture

Not sure yet if it should be userpoints + userpoints_user or uerpoints_field + userpoints. It can't be in the same module due to dependencies, is that correct?

Given that, I think having userpoints_field and depending on it makes more sense. Enabling Userpoints should get you the default experience and if you want something more advanced, then using the default field UI sounds fine to me. That will, for the moment, also allow us to directly set the values for it in the userpoints transaction storage controller.

Nice start, but there are some interesting problems ahead, like the whole categorization thing, as discussed before, it might make sense to remove that first.

+++ b/userpoints.transaction.incundefined
@@ -1430,3 +1431,22 @@ class UserpointsTransactionMetadataController extends EntityDefaultMetadataContr
+  public function views_data() {
+    $data = parent::views_data();
+
+    $data['userpoints_txn']['uid']['relationship'] = array(
+      'title' => t('User'),
+      'help' => t('Relate the userpoints transaction to the user.'),

That relation should be created by default if uid is set to type user?

+++ b/userpoints_user/userpoints_user.moduleundefined
@@ -0,0 +1,63 @@
+  foreach ($result as $record) {
+    $item['points'] += $record->points;
+    $item['max_points'] += ($record->points > 0) ? $record->points : 0;
+  }

That is not correct* and doesn't scale. Have a look at the update total logic in the storage controller to see what needs to be done here.

* It makes the same mistake as the earlier max points implementation. Once you have negative points, the max is a "sum of all positive transactions" and not "highest point amount the user ever had" anymore.

mradcliffe’s picture

StatusFileSize
new8.35 KB

Thanks for the feedback.

I think fields can be transition before categorization as the taxonomy term aggregation can be added with views. I didn't attempt anything there yet (lots of @todo). I'm trying to develop things side-by-side so that I can compare functionality.

Changes

  • It looks like we won't need a separate dependency at all because field_cache_clear() needs to be called regardless so there's no benefit of a dependency.
  • I wasn't sure if the type on uid was specifically not set to user. I changed the type to user instead.
  • I moved the max points calculation into the field update API and corrected the calculation to total historical points.
  • Expanded the field formatter a bit, but that could use some additional work.
  • Refactored the function to aggregate points based on a mix of the userpoints_user function from before and userpoints_get_current_points().
  • I kept the hook_userpoints_transaction_update/insert() as kind of an example. I'm not sure about putting it back into the storage controller in order to separate entity and field API operations. I'm sure other contrib modules would add points fields and just calculate similarly based on hook_userpoints_transaction_update/insert().

I refactored so much there wouldn't be a point to an interdiff.

mradcliffe’s picture

Status: Needs review » Needs work

I forgot to change to needs work as it still needs work. :-)

Also need to respect status in getting points.

mradcliffe’s picture

Hmm... I think the field needs to be split out into a dependency because it is now difficult to disable / uninstall userpoints.

mradcliffe’s picture

Status: Needs work » Needs review
StatusFileSize
new9.01 KB

Here's a patch splitting field into userpoints_field and then installed by userpoints so that the module can be uninstalled appropriately.

mradcliffe’s picture

StatusFileSize
new12.44 KB

Here's a patch reflecting #1870674: Categorization with default taxonomy term reference field and views #6 from months ago, which is a different direction from the above patches.

Objective of patch so far:

  • Add field definition in userpoints_field module.
  • Add default field and instance on the user entity on install and update.
  • Save points and max points values per bundle to field when a transaction is created or updated.
    • todo: need to implement hook_userpoints_transaction_delete().
  • Basic field formatter to display each field value as "bundle: points"

Status: Needs review » Needs work

The last submitted patch, 1258032-points-field-patch-4.patch, failed testing.

berdir’s picture

Looks interesting, some thoughts below.

+++ b/userpoints.installundefined
@@ -273,6 +273,28 @@ function userpoints_install() {
+    'label' => st('Points'),

Points branding is going to be an interesting challenge, we might need to implement some alter hooks so that the field label is changed to whatever is configured in the backend (later even field settings).

+++ b/userpoints.moduleundefined
@@ -608,6 +608,101 @@ function userpoints_token_info() {
+  if (isset($account->field_userpoints['und'])) {
+    foreach ($account->field_userpoints['und'] as $num => $values) {

should use the LANGUAGE_NONE constant. Wondering if we can prevent this from being made translatable, that's going to result in an interesting mess.

+++ b/userpoints.moduleundefined
@@ -608,6 +608,101 @@ function userpoints_token_info() {
+  user_save($account);
+  $account->field_userpoints['und'][$delta]['points'] = userpoints_transaction_get_points($account->uid, $entity->type);
+  $account->field_userpoints['und'][$delta]['bundle'] = $entity->type;
+  $account = user_save($account);

Not sure what you're doing with user_save() here exactly :) A user is usually saved like this: user_save($account, (array)$account). entity_save('user', $account) should take care of that weirdness?

Also, I still don't understand why this is separate from the existing logic to calculate the total that happens in in UserpointsTransactionController::updateTotals(). That's IMHO the right place for this.

+++ b/userpoints.moduleundefined
@@ -608,6 +608,101 @@ function userpoints_token_info() {
+function userpoints_transaction_get_points($uid = NULL, $type = NULL) {

Then you don't need this anymore, as we already have (quite complicated) points and max points calculation there.

+++ b/userpoints_field/userpoints_field.installundefined
@@ -0,0 +1,31 @@
+ * @file
+ *   Userpoints field schema and installation.

There shouldn't be additional spaces before the @file description

+++ b/userpoints_field/userpoints_field.moduleundefined
@@ -0,0 +1,181 @@
+      if (!isset($item['max_points']) || $item['points'] > $item['max_points']) {
+        // Set maximum points only if current points is greater.
+        $items[$delta]['max_points'] = $item['points'];

Again, we already calculate this in updateTotals(). The calculation is currently more complicated due to the special 'all' category, can probably be simplified once we remove it.

+++ b/userpoints_field/userpoints_field.moduleundefined
@@ -0,0 +1,181 @@
+      '#prefix' => '<span>' . check_plain($types[$item['bundle']]->label) . ': </span>',

Hm. Not yet 100% sure how points label branding/field label/bundle label will play together.

Using the bundle label as branding might probably make the most sense but what can we do if there is none available (for generic descriptions and so on).

Also, translations, this will need to be translatable somehow. Either i18n_string or entity/field translation.

+++ b/userpoints_field/userpoints_field.moduleundefined
@@ -0,0 +1,181 @@
+    'bundle' => array(
+      'type' => 'token',

Hm, this could actually use the entity type as type?

mradcliffe’s picture

StatusFileSize
new8.55 KB
new23.26 KB

I haven't posted a patch in a while, but I had been working on this. I am trying to keep the current taxonomy stuff untouched so that it can be migrated later.

  • Created a (temporary?) updateFieldValues method and removed hooks against userpoints transaction crud.
    • This currently is hardcoded to users due to some issues. I'm not sure if anything else should be supported in this issue even though it's the eventual goal.
  • Moved max points calculation from the field update hook to updateFieldValues.
  • Code standard fixes.
  • Entity property type for bundle column in the field schema changed to userpoints transaction type entity.
  • Added userpoints_field into system table for tests to simulate an upgrade.

I attached a git show interdiff of my past commits without the binary chunk from the patch.

mradcliffe’s picture

Status: Needs work » Needs review

Status change.

tclnj’s picture

#12: 1258032-points-field-patch-4.patch queued for re-testing.

PatchRanger’s picture

Don't you all think that such module shouldn't be called 'Userpoints' anymore? I guess the best name is just 'Points' - it is free now: https://drupal.org/project/points . In my view it should be done as a separate sandbox completely independent from Userpoints - just relying on its code by copy-paste :)

giorgio79’s picture

Issue summary: View changes

Great idea for the Points name.

Perhaps that should be used for D8, and provide a migration / update path to that module as this change wont be happening for D7 it seems :)

Anonymous’s picture

any progress on this...can't wait to test the first release?!

ibexy’s picture

any progress on this. I will help test.