Default Userprop Values

From Dreamwidth Notes
Jump to: navigation, search

Here's the problem we're solving: let's say that there's a prop that is set to a value of 'Q' for all new users, because we want 'Q' to be the default value. However, then we determine later that we actually want the default value to be 'W'. How do we change this? We can't just change every 'Q' value to be 'W', because there is no way to distinguish who actually explicitly wants their prop to be 'Q' and who just never changed away from the default. So we could change it for all new users, but that doesn't help us with the current users. And we'd just keep running into the same problem.

We solve this problem by making the default value of a prop always be an empty string (i.e. no value set). If there is an explicit value set in the prop, we know that the user wants to keep that value, and we should never change it for them. But if it's empty, we can assume that the user is probably okay with the default setting, whatever that may be.

This will also allow us to create different default values for different situations (i.e. you may want one default for personal journals and a different one for identity accounts). We will then use methods to determine what an empty prop means in practice.

Here's an example, using the opt_logcommentips userprop.

Usually, you'd get the value of this prop by doing:

$u->prop( 'opt_logcommentips' );

However, the problem with doing this is that you get the actual prop value, which then you have to work with to determine what that means if it's empty, invalid, etc. So, you should create a User method for it:

sub opt_logcommentips {
    my $u = shift;
 
    # return prop value if it exists and is valid
    my $prop_val = $u->prop( 'opt_logcommentips' );
    return $prop_val if $prop_val =~ /^[NSA]$/;
 
    # otherwise, return the default: log for all comments
    return 'A';
}

So now when you want to get the value of the prop, you do:

$u->opt_logcommentips;

Note that you should always name the method the same as the userprop; it's easier to use that way.

You can even go a step further and make methods such as:

$u->logs_ips_for( $other_u );
$u->logs_all_ips;

Whatever is relevant to your userprop. These methods would call your opt_logcommentips method.

Another tip: if you want to create a different default value for new users vs. current users, then set in %LJ::USERPROP_INIT a value of "nu_default" ("new user default") for the prop. This means that all users created after this code is live will have a default prop value of "nu_default", while all users created before will have a default prop value of nothing. Then, you can use your method to make "nu_default" mean whatever you want.

For a more complicated example of all of this, check out the LJ::User->safe_search and LJ::User->should_show_in_search_results methods. These show what creating methods for getting your prop values really allow you to do.

Extra Credit: Setting a prop value only when needed

You may have noticed that it's pretty hard to determine if a user is explicitly set a prop or is just keeping the default when we have multiple settings on one page. How do we know the difference between a user actually reading a setting and saying "I want to keep this default value" vs. a user who is just changing some other value on the page, and didn't even look at the setting we're working with? You could do use JavaScript to check if they changed the prop value, perhaps, but there's another way (note that this method does not actually exist, but it's a good example of what you can do):

sub set_opt_logcommentips {
    my ( $u, $val ) = @_;
 
    # verify that the given value is valid
    return unless $val =~ /^[NSA]$/;
 
    # get current prop value
    my $current_val = $u->opt_logcommentips;
 
    # set it if the values are different
    unless ( $current_val eq $val ) {
        $u->set_prop( opt_logcommentips => $val );
    }
 
    return;
}

So basically what's happening here is you're only setting the prop value if the value given and the derived meaning of the current prop value are different. So, if the value being set here was 'A', and the user's prop value was empty (default), the prop value wouldn't actually be changed here at all. It would stay at empty (default). However, if it was changed to 'N' it would be set, and if it was then changed back to 'A', that would be set. Then we know that this user set the prop explicitly.

Of course, this isn't full-proof; in fact, it only fixes the case of a user not really caring about what the prop is set to, but then saving the page where the prop gets set. It doesn't solve the issue of a user explicitly wanting to keep the value, but not changing it. Unfortunately, we can't yet read our users' minds, so there's not much we can do about that case without making things more complicated (i.e. making a "current default" option for them to choose).

This method of saving prop values may not be the correct thing to do all the time--in fact, this method is only used in a very small number of cases right now. It really depends on whether it's more important to be able to change people's default preferences later on, or if it's more important to "play it safe" and keep whatever the user had set when they saved the page (even if it was the default value). Which of these is more important probably depends on the specific prop being set. If you're not sure, it's probably safest to not use this method of setting a prop, and instead always set whatever the form submits (assuming it's valid, of course).