Setting.pm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. # -*- Mode: perl; indent-tabs-mode: nil -*-
  2. #
  3. # The contents of this file are subject to the Mozilla Public
  4. # License Version 1.1 (the "License"); you may not use this file
  5. # except in compliance with the License. You may obtain a copy of
  6. # the License at http://www.mozilla.org/MPL/
  7. #
  8. # Software distributed under the License is distributed on an "AS
  9. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10. # implied. See the License for the specific language governing
  11. # rights and limitations under the License.
  12. #
  13. # The Original Code is the Bugzilla Bug Tracking System.
  14. #
  15. # Contributor(s): Shane H. W. Travis <travis@sedsystems.ca>
  16. # Max Kanat-Alexander <mkanat@bugzilla.org>
  17. # Marc Schumann <wurblzap@gmail.com>
  18. # Frédéric Buclin <LpSolit@gmail.com>
  19. package Bugzilla::User::Setting;
  20. use strict;
  21. use base qw(Exporter);
  22. # Module stuff
  23. @Bugzilla::User::Setting::EXPORT = qw(get_all_settings get_defaults
  24. add_setting);
  25. use Bugzilla::Error;
  26. use Bugzilla::Util qw(trick_taint get_text);
  27. ###############################
  28. ### Module Initialization ###
  29. ###############################
  30. sub new {
  31. my $invocant = shift;
  32. my $setting_name = shift;
  33. my $user_id = shift;
  34. my $class = ref($invocant) || $invocant;
  35. my $subclass = '';
  36. # Create a ref to an empty hash and bless it
  37. my $self = {};
  38. my $dbh = Bugzilla->dbh;
  39. # Confirm that the $setting_name is properly formed;
  40. # if not, throw a code error.
  41. #
  42. # NOTE: due to the way that setting names are used in templates,
  43. # they must conform to to the limitations set for HTML NAMEs and IDs.
  44. #
  45. if ( !($setting_name =~ /^[a-zA-Z][-.:\w]*$/) ) {
  46. ThrowCodeError("setting_name_invalid", { name => $setting_name });
  47. }
  48. # If there were only two parameters passed in, then we need
  49. # to retrieve the information for this setting ourselves.
  50. if (scalar @_ == 0) {
  51. my ($default, $is_enabled, $value);
  52. ($default, $is_enabled, $value, $subclass) =
  53. $dbh->selectrow_array(
  54. q{SELECT default_value, is_enabled, setting_value, subclass
  55. FROM setting
  56. LEFT JOIN profile_setting
  57. ON setting.name = profile_setting.setting_name
  58. WHERE name = ?
  59. AND profile_setting.user_id = ?},
  60. undef,
  61. $setting_name, $user_id);
  62. # if not defined, then grab the default value
  63. if (! defined $value) {
  64. ($default, $is_enabled, $subclass) =
  65. $dbh->selectrow_array(
  66. q{SELECT default_value, is_enabled, subclass
  67. FROM setting
  68. WHERE name = ?},
  69. undef,
  70. $setting_name);
  71. }
  72. $self->{'is_enabled'} = $is_enabled;
  73. $self->{'default_value'} = $default;
  74. # IF the setting is enabled, AND the user has chosen a setting
  75. # THEN return that value
  76. # ELSE return the site default, and note that it is the default.
  77. if ( ($is_enabled) && (defined $value) ) {
  78. $self->{'value'} = $value;
  79. } else {
  80. $self->{'value'} = $default;
  81. $self->{'isdefault'} = 1;
  82. }
  83. }
  84. else {
  85. # If the values were passed in, simply assign them and return.
  86. $self->{'is_enabled'} = shift;
  87. $self->{'default_value'} = shift;
  88. $self->{'value'} = shift;
  89. $self->{'is_default'} = shift;
  90. $subclass = shift;
  91. }
  92. if ($subclass) {
  93. eval('require ' . $class . '::' . $subclass);
  94. $@ && ThrowCodeError('setting_subclass_invalid',
  95. {'subclass' => $subclass});
  96. $class = $class . '::' . $subclass;
  97. }
  98. bless($self, $class);
  99. $self->{'_setting_name'} = $setting_name;
  100. $self->{'_user_id'} = $user_id;
  101. return $self;
  102. }
  103. ###############################
  104. ### Subroutine Definitions ###
  105. ###############################
  106. sub add_setting {
  107. my ($name, $values, $default_value, $subclass, $force_check) = @_;
  108. my $dbh = Bugzilla->dbh;
  109. my $exists = _setting_exists($name);
  110. return if ($exists && !$force_check);
  111. ($name && $default_value)
  112. || ThrowCodeError("setting_info_invalid");
  113. if ($exists) {
  114. # If this setting exists, we delete it and regenerate it.
  115. $dbh->do('DELETE FROM setting_value WHERE name = ?', undef, $name);
  116. $dbh->do('DELETE FROM setting WHERE name = ?', undef, $name);
  117. # Remove obsolete user preferences for this setting.
  118. if (defined $values && scalar(@$values)) {
  119. my $list = join(', ', map {$dbh->quote($_)} @$values);
  120. $dbh->do("DELETE FROM profile_setting
  121. WHERE setting_name = ? AND setting_value NOT IN ($list)",
  122. undef, $name);
  123. }
  124. }
  125. else {
  126. print get_text('install_setting_new', { name => $name }) . "\n";
  127. }
  128. $dbh->do(q{INSERT INTO setting (name, default_value, is_enabled, subclass)
  129. VALUES (?, ?, 1, ?)},
  130. undef, ($name, $default_value, $subclass));
  131. my $sth = $dbh->prepare(q{INSERT INTO setting_value (name, value, sortindex)
  132. VALUES (?, ?, ?)});
  133. my $sortindex = 5;
  134. foreach my $key (@$values){
  135. $sth->execute($name, $key, $sortindex);
  136. $sortindex += 5;
  137. }
  138. }
  139. sub get_all_settings {
  140. my ($user_id) = @_;
  141. my $settings = get_defaults($user_id); # first get the defaults
  142. my $dbh = Bugzilla->dbh;
  143. my $sth = $dbh->prepare(
  144. q{SELECT name, default_value, is_enabled, setting_value, subclass
  145. FROM setting
  146. LEFT JOIN profile_setting
  147. ON setting.name = profile_setting.setting_name
  148. WHERE profile_setting.user_id = ?
  149. ORDER BY name});
  150. $sth->execute($user_id);
  151. while (my ($name, $default_value, $is_enabled, $value, $subclass)
  152. = $sth->fetchrow_array())
  153. {
  154. my $is_default;
  155. if ( ($is_enabled) && (defined $value) ) {
  156. $is_default = 0;
  157. } else {
  158. $value = $default_value;
  159. $is_default = 1;
  160. }
  161. $settings->{$name} = new Bugzilla::User::Setting(
  162. $name, $user_id, $is_enabled,
  163. $default_value, $value, $is_default, $subclass);
  164. }
  165. return $settings;
  166. }
  167. sub get_defaults {
  168. my ($user_id) = @_;
  169. my $dbh = Bugzilla->dbh;
  170. my $default_settings = {};
  171. $user_id ||= 0;
  172. my $sth = $dbh->prepare(q{SELECT name, default_value, is_enabled, subclass
  173. FROM setting
  174. ORDER BY name});
  175. $sth->execute();
  176. while (my ($name, $default_value, $is_enabled, $subclass)
  177. = $sth->fetchrow_array())
  178. {
  179. $default_settings->{$name} = new Bugzilla::User::Setting(
  180. $name, $user_id, $is_enabled, $default_value, $default_value, 1,
  181. $subclass);
  182. }
  183. return $default_settings;
  184. }
  185. sub set_default {
  186. my ($setting_name, $default_value, $is_enabled) = @_;
  187. my $dbh = Bugzilla->dbh;
  188. my $sth = $dbh->prepare(q{UPDATE setting
  189. SET default_value = ?, is_enabled = ?
  190. WHERE name = ?});
  191. $sth->execute($default_value, $is_enabled, $setting_name);
  192. }
  193. sub _setting_exists {
  194. my ($setting_name) = @_;
  195. my $dbh = Bugzilla->dbh;
  196. return $dbh->selectrow_arrayref(
  197. "SELECT 1 FROM setting WHERE name = ?", undef, $setting_name) || 0;
  198. }
  199. sub legal_values {
  200. my ($self) = @_;
  201. return $self->{'legal_values'} if defined $self->{'legal_values'};
  202. my $dbh = Bugzilla->dbh;
  203. $self->{'legal_values'} = $dbh->selectcol_arrayref(
  204. q{SELECT value
  205. FROM setting_value
  206. WHERE name = ?
  207. ORDER BY sortindex},
  208. undef, $self->{'_setting_name'});
  209. return $self->{'legal_values'};
  210. }
  211. sub validate_value {
  212. my $self = shift;
  213. if (grep(/^$_[0]$/, @{$self->legal_values()})) {
  214. trick_taint($_[0]);
  215. }
  216. else {
  217. ThrowCodeError('setting_value_invalid',
  218. {'name' => $self->{'_setting_name'},
  219. 'value' => $_[0]});
  220. }
  221. }
  222. sub reset_to_default {
  223. my ($self) = @_;
  224. my $dbh = Bugzilla->dbh;
  225. my $sth = $dbh->do(q{ DELETE
  226. FROM profile_setting
  227. WHERE setting_name = ?
  228. AND user_id = ?},
  229. undef, $self->{'_setting_name'}, $self->{'_user_id'});
  230. $self->{'value'} = $self->{'default_value'};
  231. $self->{'is_default'} = 1;
  232. }
  233. sub set {
  234. my ($self, $value) = @_;
  235. my $dbh = Bugzilla->dbh;
  236. my $query;
  237. if ($self->{'is_default'}) {
  238. $query = q{INSERT INTO profile_setting
  239. (setting_value, setting_name, user_id)
  240. VALUES (?,?,?)};
  241. } else {
  242. $query = q{UPDATE profile_setting
  243. SET setting_value = ?
  244. WHERE setting_name = ?
  245. AND user_id = ?};
  246. }
  247. $dbh->do($query, undef, $value, $self->{'_setting_name'}, $self->{'_user_id'});
  248. $self->{'value'} = $value;
  249. $self->{'is_default'} = 0;
  250. }
  251. 1;
  252. __END__
  253. =head1 NAME
  254. Bugzilla::User::Setting - Object for a user preference setting
  255. =head1 SYNOPSIS
  256. Setting.pm creates a setting object, which is a hash containing the user
  257. preference information for a single preference for a single user. These
  258. are usually accessed through the "settings" object of a user, and not
  259. directly.
  260. =head1 DESCRIPTION
  261. use Bugzilla::User::Setting;
  262. my $settings;
  263. $settings->{$setting_name} = new Bugzilla::User::Setting(
  264. $setting_name, $user_id);
  265. OR
  266. $settings->{$setting_name} = new Bugzilla::User::Setting(
  267. $setting_name, $user_id, $is_enabled,
  268. $default_value, $value, $is_default);
  269. =head1 CLASS FUNCTIONS
  270. =over 4
  271. =item C<add_setting($name, \@values, $default_value, $subclass, $force_check)>
  272. Description: Checks for the existence of a setting, and adds it
  273. to the database if it does not yet exist.
  274. Params: C<$name> - string - the name of the new setting
  275. C<$values> - arrayref - contains the new choices
  276. for the new Setting.
  277. C<$default_value> - string - the site default
  278. C<$subclass> - string - name of the module returning
  279. the list of valid values. This means legal values are
  280. not stored in the DB.
  281. C<$force_check> - boolean - when true, the existing setting
  282. and all its values are deleted and replaced by new data.
  283. Returns: a pointer to a hash of settings
  284. =item C<get_all_settings($user_id)>
  285. Description: Provides the user's choices for each setting in the
  286. system; if the user has made no choice, uses the site
  287. default instead.
  288. Params: C<$user_id> - integer - the user id.
  289. Returns: a pointer to a hash of settings
  290. =item C<get_defaults($user_id)>
  291. Description: When a user is not logged in, they must use the site
  292. defaults for every settings; this subroutine provides them.
  293. Params: C<$user_id> (optional) - integer - the user id. Note that
  294. this optional parameter is mainly for internal use only.
  295. Returns: A pointer to a hash of settings. If $user_id was passed, set
  296. the user_id value for each setting.
  297. =item C<set_default($setting_name, $default_value, $is_enabled)>
  298. Description: Sets the global default for a given setting. Also sets
  299. whether users are allowed to choose their own value for
  300. this setting, or if they must use the global default.
  301. Params: C<$setting_name> - string - the name of the setting
  302. C<$default_value> - string - the new default value for this setting
  303. C<$is_enabled> - boolean - if false, all users must use the global default
  304. Returns: nothing
  305. =begin private
  306. =item C<_setting_exists>
  307. Description: Determines if a given setting exists in the database.
  308. Params: C<$setting_name> - string - the setting name
  309. Returns: boolean - true if the setting already exists in the DB.
  310. =back
  311. =end private
  312. =head1 METHODS
  313. =over 4
  314. =item C<legal_values($setting_name)>
  315. Description: Returns all legal values for this setting
  316. Params: none
  317. Returns: A reference to an array containing all legal values
  318. =item C<validate_value>
  319. Description: Determines whether a value is valid for the setting
  320. by checking against the list of legal values.
  321. Untaints the parameter if the value is indeed valid,
  322. and throws a setting_value_invalid code error if not.
  323. Params: An lvalue containing a candidate for a setting value
  324. Returns: nothing
  325. =item C<reset_to_default>
  326. Description: If a user chooses to use the global default for a given
  327. setting, their saved entry is removed from the database via
  328. this subroutine.
  329. Params: none
  330. Returns: nothing
  331. =item C<set($value)>
  332. Description: If a user chooses to use their own value rather than the
  333. global value for a given setting, OR changes their value for
  334. a given setting, this subroutine is called to insert or
  335. update the database as appropriate.
  336. Params: C<$value> - string - the new value for this setting for this user.
  337. Returns: nothing
  338. =back