How to keep track of EOT time and Member level after expiry?

When a member reaches their EOT date, they are automatically demoted to level 0 and the EOT time seems to be cleared.

We need to allow members a 3 month window to pay again and be re-instated at their previous membership level.

Can anyone point me in the right direction to achieve this?

  1. We need to keep track of their EOT time for 3 months after it has passed.

  2. We need to keep track of their previous member level.

  3. We need to re-instate them at their previous member level when the pay, if it is within the 3 month window.

Thanks!

  1. and 2) s2M have “logs” for each member for the time the member changes levels and gets/loses ccaps. This is saved in member’s options, but right now I can not remember the exactly name of the option.
  2. That needs custom code. I am curious to do this, long time ago, can do for you, if you want.

Hi, thanks for the reply.
I could use some help with the custom code, and also knowing the membership options names for the log of those entries and how to access them would be great.
Thanks.

I do see an administrative note added by S2: Demoted by s2Member: Mon Jul 11, 2016 6:28 pm UTC.
So I’m guessing this indicates the EOT time, however it doesn’t mention the level they were demoted from. The notes field could have a lot of other things in it though, so doesn’t make it practical to use for any kind of conditional checks.

Here is a rough version of what I’m trying to do:

Create a page that is for subscriber level users only.

if (get_expired_EOT_time() <= 90-days){
  get_previous_user_level();
     if(get_previous_user_level() == 1){
        [renew-at-level-1]
     }
     if(get_previous_user_level() == 2){
        [renew-at-level-2]
     }
//etc
}

It would also be extremely useful if we could add custom profile fields that only admin could see with the expired EOT time and the previous member level.

No, not in “administrative notes”, not in the user account page at all. You should look in “usermeta” table, find “s2member_access_cap_times” (both table and this name have your “DB prefix”).

Okay that is helpful. I think I’m getting close on this.

I’m trying to make sense of the data in that table which looks like a timestamp and a level, but why so many levels for a user that just got demoted? It looks like a countdown but level 10 is actually next to level 1 so it is hard to say which was the last one… :

a:25:{s:15:"1468261436.0049";s:6:"level0";s:15:"1468261436.0050";s:6:"level1";s:15:"1468261436.0051";s:6:"level2";s:15:"1468261436.0052";s:6:"level3";s:15:"1468261436.0053";s:6:"level4";s:15:"1468261436.0054";s:6:"level5";s:15:"1468261436.0055";s:6:"level6";s:15:"1468261436.0056";s:6:"level7";s:15:"1468261436.0057";s:6:"level8";s:15:"1468261436.0058";s:6:"level9";s:15:"1468261436.0059";s:7:"level10";s:15:"1468261436.0060";s:7:"level11";s:15:"1470848564.0005";s:7:"level12";s:15:"1470848613.0005";s:8:"-level12";s:15:"1471705830.0001";s:7:"-level1";s:15:"1471705830.0002";s:8:"-level10";s:15:"1471705830.0003";s:8:"-level11";s:15:"1471705830.0004";s:7:"-level2";s:15:"1471705830.0005";s:7:"-level3";s:15:"1471705830.0006";s:7:"-level4";s:15:"1471705830.0007";s:7:"-level5";s:15:"1471705830.0008";s:7:"-level6";s:15:"1471705830.0009";s:7:"-level7";s:15:"1471705830.0010";s:7:"-level8";s:15:"1471705830.0011";s:7:"-level9";}

Edit: I also found this for the last EOT time:

`$lastEOT = get_user_option('s2member_last_auto_eot_time', $user_id);`

I tried to do something like this:

        $ac_times = c_ws_plugin__s2member_access_cap_times::get_access_cap_times($user_id);
        $lastcapLevel = end($ac_times);

… which gets the last entry in the s2member_access_cap_times data, but it doesn’t work to show the last level.

I just found this option: s2member_access_cap_times

so now I can do this:

$accessCaps = get_user_option('s2member_access_cap_times', $user_id);
and get the last cap like this:
$lastCap= end($accessCaps);

This will output the last cap up until level9, however beyond level 9 it the end() ffunction no longer works because…

when I output the entire array for a user that was at level 11 and was demoted to level 0 here’s what it looks like
foreach($accessCaps as $key => $value) { echo "$key is at $value<br>"; }

1473520578.0001 is at -level1
1473520578.0002 is at -level10
1473520578.0003 is at -level11
1473520578.0004 is at -level2
1473520578.0005 is at -level3
1473520578.0006 is at -level4
1473520578.0007 is at -level5
1473520578.0008 is at -level6
1473520578.0009 is at -level7
1473520578.0010 is at -level8
1473520578.0011 is at -level9

echo end($accessCaps);
this will echo ’ -level9 ’ because it put level 10 and 11 next to level one sequentially.

So, it doesn’t look like there is any way to actually get the last member level with this function.

Is there another way to get the last role that a user had before they were demoted or changed?

Sort them by time (as float point values), find the sub-array with same integer part like the bigger timestamp, then sort this new array by values (levels), and you will have bigger level (L11 in your case). This is because levels in s2M are inherited, means a member at L3 have access to all content of L2, L1 and L0 by default.

Hi Krum,
Thanks. I understand conceptually what you mean, but I don’t know how to write the code to do that. Is there a simpler way to do this? Like filter the EOT depreciation action and copy the user role into a custom field?

Sure, possible. If you think it’s “simpler”… I would save it in custom meta, instead in a custom profile field - easer, and independent from s2M. May use Notifications, maybe, but not sure if you can find the old level of the member, you must check.

Another way: must save the current level of the member when account is created or edited, but not at EOT (will show L0).

Hi, yeah only ‘simpler’ in that I think I understand the code to do it. I don’t know what happens on EOT, if there is a way to filter it and catch it before the demotion and at that point copy the user level to a custom field.
If you can point me in the direction of how to get the same integer par and sort that array by its values I would try that as well.

Thanks.

Try this, should give you “-level11”:

$accessCaps = get_user_option('s2member_access_cap_times', $user_id);
// Sort by time and find the integer part
list($intTime,) = explode(".", end(array_keys(ksort($accessCaps))));
$lastCaps = array();
// find the sub-array with same integer part
foreach(krsort($accessCaps) as $k => $v) if(false !== strpos($k, $intTime)) $lastCaps[] = $v;
// sort this new array by values and find the bigger level
$lastCap= end(sort($lastCaps));

Hi and thanks, it is giving some errors:

Warning: array_keys() expects parameter 1 to be array, boolean given on line 3
Strict Standards: Only variables should be passed by reference on line 3
Warning: end() expects parameter 1 to be array, null given on line 3
Warning: Invalid argument supplied for foreach() on line 6
Strict Standards: Only variables should be passed by reference on line 8
Warning: end() expects parameter 1 to be array, boolean given on line 8

  1. $accessCaps = get_user_option(‘s2member_access_cap_times’, $user_id);
  2. // Sort by time and find the integer part
  3. list($intTime,) = explode(".", end(array_keys(ksort($accessCaps))));
  4. $lastCaps = array();
  5. // find the sub-array with same integer part
  6. foreach(krsort($accessCaps) as $k => $v) if(false !== strpos($k, $intTime)) $lastCaps[] = $v;
  7. // sort this new array by values and find the bigger level
  8. $lastCap= end(sort($lastCaps));

My fault, sorry, I am not tested it myself. New code:

$accessCaps = get_user_option('s2member_access_cap_times', $user_id);
// Sort by time
ksort($accessCaps);
// find the integer part
list($intTime,) = explode(".", end(array_keys($accessCaps)));
$lastCaps = array();
// find the sub-array with same integer part
krsort($accessCaps);
foreach($accessCaps as $k => $v) if(false !== strpos($k, $intTime)) $lastCaps[] = $v;
// sort this new array by values
sort($lastCaps);
// find the bigger level
$lastCap= end($lastCaps);

Still not tested myself, so tell me results, please.

Strict Standards: Only variables should be passed by reference for:

list($intTime,) = explode(".", end(array_keys($accessCaps)));

OK, new code:

$accessCaps = get_user_option('s2member_access_cap_times', $user_id);
// Sort by time
ksort($accessCaps);
// find the integer part
$keysCaps = array_keys($accessCaps);
list($intTime,) = explode(".", end($keysCaps));
$lastCaps = array();
// find the sub-array with same integer part
krsort($accessCaps);
foreach($accessCaps as $k => $v) if(false !== strpos($k, $intTime)) $lastCaps[] = $v;
// sort this new array by values
sort($lastCaps);
// find the bigger level
$lastCap= end($lastCaps);

Hi, Thanks but it still looks off, here’s the output

1474002116.0001 is at -level1
1474002116.0002 is at -level10
1474002116.0003 is at -level2
1474002116.0004 is at -level3
1474002116.0005 is at -level4
1474002116.0006 is at -level5
1474002116.0007 is at -level6
1474002116.0008 is at -level7
1474002116.0009 is at -level8
1474002116.0010 is at -level9
$lastCap = -level9

But the last cap was really level 10

Please change the row

sort($lastCaps);

to

sort($lastCaps, SORT_STRING);

and let we see results.