442 lines
13 KiB
Perl
442 lines
13 KiB
Perl
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
#
|
|
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
|
# defined by the Mozilla Public License, v. 2.0.
|
|
|
|
|
|
package Bugzilla::User::Setting;
|
|
|
|
use 5.10.1;
|
|
use strict;
|
|
use warnings;
|
|
|
|
use parent qw(Exporter);
|
|
|
|
|
|
# Module stuff
|
|
@Bugzilla::User::Setting::EXPORT = qw(
|
|
get_all_settings
|
|
get_defaults
|
|
add_setting
|
|
clear_settings_cache
|
|
);
|
|
|
|
use Bugzilla::Error;
|
|
use Bugzilla::Util qw(trick_taint get_text);
|
|
|
|
###############################
|
|
### Module Initialization ###
|
|
###############################
|
|
|
|
sub new {
|
|
my $invocant = shift;
|
|
my $setting_name = shift;
|
|
my $user_id = shift;
|
|
|
|
my $class = ref($invocant) || $invocant;
|
|
my $subclass = '';
|
|
|
|
# Create a ref to an empty hash and bless it
|
|
my $self = {};
|
|
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
# Confirm that the $setting_name is properly formed;
|
|
# if not, throw a code error.
|
|
#
|
|
# NOTE: due to the way that setting names are used in templates,
|
|
# they must conform to to the limitations set for HTML NAMEs and IDs.
|
|
#
|
|
if ( !($setting_name =~ /^[a-zA-Z][-.:\w]*$/) ) {
|
|
ThrowCodeError("setting_name_invalid", { name => $setting_name });
|
|
}
|
|
|
|
# If there were only two parameters passed in, then we need
|
|
# to retrieve the information for this setting ourselves.
|
|
if (scalar @_ == 0) {
|
|
|
|
my ($default, $is_enabled, $value);
|
|
($default, $is_enabled, $value, $subclass) =
|
|
$dbh->selectrow_array(
|
|
q{SELECT default_value, is_enabled, setting_value, subclass
|
|
FROM setting
|
|
LEFT JOIN profile_setting
|
|
ON setting.name = profile_setting.setting_name
|
|
WHERE name = ?
|
|
AND profile_setting.user_id = ?},
|
|
undef,
|
|
$setting_name, $user_id);
|
|
|
|
# if not defined, then grab the default value
|
|
if (! defined $value) {
|
|
($default, $is_enabled, $subclass) =
|
|
$dbh->selectrow_array(
|
|
q{SELECT default_value, is_enabled, subclass
|
|
FROM setting
|
|
WHERE name = ?},
|
|
undef,
|
|
$setting_name);
|
|
}
|
|
|
|
$self->{'is_enabled'} = $is_enabled;
|
|
$self->{'default_value'} = $default;
|
|
|
|
# IF the setting is enabled, AND the user has chosen a setting
|
|
# THEN return that value
|
|
# ELSE return the site default, and note that it is the default.
|
|
if ( ($is_enabled) && (defined $value) ) {
|
|
$self->{'value'} = $value;
|
|
} else {
|
|
$self->{'value'} = $default;
|
|
$self->{'isdefault'} = 1;
|
|
}
|
|
}
|
|
else {
|
|
# If the values were passed in, simply assign them and return.
|
|
$self->{'is_enabled'} = shift;
|
|
$self->{'default_value'} = shift;
|
|
$self->{'value'} = shift;
|
|
$self->{'is_default'} = shift;
|
|
$subclass = shift;
|
|
}
|
|
if ($subclass) {
|
|
eval('require ' . $class . '::' . $subclass);
|
|
$@ && ThrowCodeError('setting_subclass_invalid',
|
|
{'subclass' => $subclass});
|
|
$class = $class . '::' . $subclass;
|
|
}
|
|
bless($self, $class);
|
|
|
|
$self->{'_setting_name'} = $setting_name;
|
|
$self->{'_user_id'} = $user_id;
|
|
|
|
return $self;
|
|
}
|
|
|
|
###############################
|
|
### Subroutine Definitions ###
|
|
###############################
|
|
|
|
sub add_setting {
|
|
my ($name, $values, $default_value, $subclass, $force_check,
|
|
$silently) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
my $exists = _setting_exists($name);
|
|
return if ($exists && !$force_check);
|
|
|
|
($name && length( $default_value // '' ))
|
|
|| ThrowCodeError("setting_info_invalid");
|
|
|
|
if ($exists) {
|
|
# If this setting exists, we delete it and regenerate it.
|
|
$dbh->do('DELETE FROM setting_value WHERE name = ?', undef, $name);
|
|
$dbh->do('DELETE FROM setting WHERE name = ?', undef, $name);
|
|
# Remove obsolete user preferences for this setting.
|
|
if (defined $values && scalar(@$values)) {
|
|
my $list = join(', ', map {$dbh->quote($_)} @$values);
|
|
$dbh->do("DELETE FROM profile_setting
|
|
WHERE setting_name = ? AND setting_value NOT IN ($list)",
|
|
undef, $name);
|
|
}
|
|
}
|
|
elsif (!$silently) {
|
|
print get_text('install_setting_new', { name => $name }) . "\n";
|
|
}
|
|
$dbh->do(q{INSERT INTO setting (name, default_value, is_enabled, subclass)
|
|
VALUES (?, ?, 1, ?)},
|
|
undef, ($name, $default_value, $subclass));
|
|
|
|
my $sth = $dbh->prepare(q{INSERT INTO setting_value (name, value, sortindex)
|
|
VALUES (?, ?, ?)});
|
|
|
|
my $sortindex = 5;
|
|
foreach my $key (@$values){
|
|
$sth->execute($name, $key, $sortindex);
|
|
$sortindex += 5;
|
|
}
|
|
}
|
|
|
|
sub get_all_settings {
|
|
my ($user_id) = @_;
|
|
my $settings = {};
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
my $cache_key = "user_settings.$user_id";
|
|
my $rows = Bugzilla->memcached->get_config({ key => $cache_key });
|
|
if (!$rows) {
|
|
$rows = $dbh->selectall_arrayref(
|
|
q{SELECT name, default_value, is_enabled, setting_value, subclass
|
|
FROM setting
|
|
LEFT JOIN profile_setting
|
|
ON setting.name = profile_setting.setting_name
|
|
AND profile_setting.user_id = ?}, undef, ($user_id));
|
|
Bugzilla->memcached->set_config({ key => $cache_key, data => $rows });
|
|
}
|
|
|
|
foreach my $row (@$rows) {
|
|
my ($name, $default_value, $is_enabled, $value, $subclass) = @$row;
|
|
|
|
my $is_default;
|
|
|
|
if ( ($is_enabled) && (defined $value) ) {
|
|
$is_default = 0;
|
|
} else {
|
|
$value = $default_value;
|
|
$is_default = 1;
|
|
}
|
|
|
|
$settings->{$name} = new Bugzilla::User::Setting(
|
|
$name, $user_id, $is_enabled,
|
|
$default_value, $value, $is_default, $subclass);
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
sub clear_settings_cache {
|
|
my ($user_id) = @_;
|
|
Bugzilla->memcached->clear_config({ key => "user_settings.$user_id" });
|
|
}
|
|
|
|
sub get_defaults {
|
|
my ($user_id) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $default_settings = {};
|
|
|
|
$user_id ||= 0;
|
|
|
|
my $rows = $dbh->selectall_arrayref(q{SELECT name, default_value, is_enabled, subclass
|
|
FROM setting});
|
|
|
|
foreach my $row (@$rows) {
|
|
my ($name, $default_value, $is_enabled, $subclass) = @$row;
|
|
|
|
$default_settings->{$name} = new Bugzilla::User::Setting(
|
|
$name, $user_id, $is_enabled, $default_value, $default_value, 1,
|
|
$subclass);
|
|
}
|
|
|
|
return $default_settings;
|
|
}
|
|
|
|
sub set_default {
|
|
my ($setting_name, $default_value, $is_enabled) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
my $sth = $dbh->prepare(q{UPDATE setting
|
|
SET default_value = ?, is_enabled = ?
|
|
WHERE name = ?});
|
|
$sth->execute($default_value, $is_enabled, $setting_name);
|
|
}
|
|
|
|
sub _setting_exists {
|
|
my ($setting_name) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
return $dbh->selectrow_arrayref(
|
|
"SELECT 1 FROM setting WHERE name = ?", undef, $setting_name) || 0;
|
|
}
|
|
|
|
|
|
sub legal_values {
|
|
my ($self) = @_;
|
|
|
|
return $self->{'legal_values'} if defined $self->{'legal_values'};
|
|
|
|
my $dbh = Bugzilla->dbh;
|
|
$self->{'legal_values'} = $dbh->selectcol_arrayref(
|
|
q{SELECT value
|
|
FROM setting_value
|
|
WHERE name = ?
|
|
ORDER BY sortindex},
|
|
undef, $self->{'_setting_name'});
|
|
|
|
return $self->{'legal_values'};
|
|
}
|
|
|
|
sub validate_value {
|
|
my $self = shift;
|
|
|
|
if (grep(/^$_[0]$/, @{$self->legal_values()})) {
|
|
trick_taint($_[0]);
|
|
}
|
|
else {
|
|
ThrowCodeError('setting_value_invalid',
|
|
{'name' => $self->{'_setting_name'},
|
|
'value' => $_[0]});
|
|
}
|
|
}
|
|
|
|
sub reset_to_default {
|
|
my ($self) = @_;
|
|
|
|
my $dbh = Bugzilla->dbh;
|
|
my $sth = $dbh->do(q{ DELETE
|
|
FROM profile_setting
|
|
WHERE setting_name = ?
|
|
AND user_id = ?},
|
|
undef, $self->{'_setting_name'}, $self->{'_user_id'});
|
|
$self->{'value'} = $self->{'default_value'};
|
|
$self->{'is_default'} = 1;
|
|
}
|
|
|
|
sub set {
|
|
my ($self, $value) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $query;
|
|
|
|
if ($self->{'is_default'}) {
|
|
$query = q{INSERT INTO profile_setting
|
|
(setting_value, setting_name, user_id)
|
|
VALUES (?,?,?)};
|
|
} else {
|
|
$query = q{UPDATE profile_setting
|
|
SET setting_value = ?
|
|
WHERE setting_name = ?
|
|
AND user_id = ?};
|
|
}
|
|
$dbh->do($query, undef, $value, $self->{'_setting_name'}, $self->{'_user_id'});
|
|
|
|
$self->{'value'} = $value;
|
|
$self->{'is_default'} = 0;
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Bugzilla::User::Setting - Object for a user preference setting
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
Setting.pm creates a setting object, which is a hash containing the user
|
|
preference information for a single preference for a single user. These
|
|
are usually accessed through the "settings" object of a user, and not
|
|
directly.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
use Bugzilla::User::Setting;
|
|
my $settings;
|
|
|
|
$settings->{$setting_name} = new Bugzilla::User::Setting(
|
|
$setting_name, $user_id);
|
|
|
|
OR
|
|
|
|
$settings->{$setting_name} = new Bugzilla::User::Setting(
|
|
$setting_name, $user_id, $is_enabled,
|
|
$default_value, $value, $is_default);
|
|
|
|
=head1 CLASS FUNCTIONS
|
|
|
|
=over 4
|
|
|
|
=item C<add_setting($name, \@values, $default_value, $subclass, $force_check)>
|
|
|
|
Description: Checks for the existence of a setting, and adds it
|
|
to the database if it does not yet exist.
|
|
|
|
Params: C<$name> - string - the name of the new setting
|
|
C<$values> - arrayref - contains the new choices
|
|
for the new Setting.
|
|
C<$default_value> - string - the site default
|
|
C<$subclass> - string - name of the module returning
|
|
the list of valid values. This means legal values are
|
|
not stored in the DB.
|
|
C<$force_check> - boolean - when true, the existing setting
|
|
and all its values are deleted and replaced by new data.
|
|
|
|
Returns: a pointer to a hash of settings
|
|
|
|
|
|
=item C<get_all_settings($user_id)>
|
|
|
|
Description: Provides the user's choices for each setting in the
|
|
system; if the user has made no choice, uses the site
|
|
default instead.
|
|
Params: C<$user_id> - integer - the user id.
|
|
Returns: a pointer to a hash of settings
|
|
|
|
=item C<get_defaults($user_id)>
|
|
|
|
Description: When a user is not logged in, they must use the site
|
|
defaults for every settings; this subroutine provides them.
|
|
Params: C<$user_id> (optional) - integer - the user id. Note that
|
|
this optional parameter is mainly for internal use only.
|
|
Returns: A pointer to a hash of settings. If $user_id was passed, set
|
|
the user_id value for each setting.
|
|
|
|
=item C<set_default($setting_name, $default_value, $is_enabled)>
|
|
|
|
Description: Sets the global default for a given setting. Also sets
|
|
whether users are allowed to choose their own value for
|
|
this setting, or if they must use the global default.
|
|
Params: C<$setting_name> - string - the name of the setting
|
|
C<$default_value> - string - the new default value for this setting
|
|
C<$is_enabled> - boolean - if false, all users must use the global default
|
|
Returns: nothing
|
|
|
|
=item C<clear_settings_cache($user_id)>
|
|
|
|
Description: Clears cached settings data for the specified user. Must be
|
|
called after updating any user's setting.
|
|
Params: C<$user_id> - integer - the user id.
|
|
Returns: nothing
|
|
|
|
=begin private
|
|
|
|
=item C<_setting_exists>
|
|
|
|
Description: Determines if a given setting exists in the database.
|
|
Params: C<$setting_name> - string - the setting name
|
|
Returns: boolean - true if the setting already exists in the DB.
|
|
|
|
=end private
|
|
|
|
=back
|
|
|
|
=head1 METHODS
|
|
|
|
=over 4
|
|
|
|
=item C<legal_values($setting_name)>
|
|
|
|
Description: Returns all legal values for this setting
|
|
Params: none
|
|
Returns: A reference to an array containing all legal values
|
|
|
|
=item C<validate_value>
|
|
|
|
Description: Determines whether a value is valid for the setting
|
|
by checking against the list of legal values.
|
|
Untaints the parameter if the value is indeed valid,
|
|
and throws a setting_value_invalid code error if not.
|
|
Params: An lvalue containing a candidate for a setting value
|
|
Returns: nothing
|
|
|
|
=item C<reset_to_default>
|
|
|
|
Description: If a user chooses to use the global default for a given
|
|
setting, their saved entry is removed from the database via
|
|
this subroutine.
|
|
Params: none
|
|
Returns: nothing
|
|
|
|
=item C<set($value)>
|
|
|
|
Description: If a user chooses to use their own value rather than the
|
|
global value for a given setting, OR changes their value for
|
|
a given setting, this subroutine is called to insert or
|
|
update the database as appropriate.
|
|
Params: C<$value> - string - the new value for this setting for this user.
|
|
Returns: nothing
|
|
|
|
=back
|