Closed Bug 98123 Opened 23 years ago Closed 20 years ago

Create a user preferences infrastructure (became 'General Settings')

Categories

(Bugzilla :: User Accounts, enhancement, P1)

enhancement

Tracking

()

RESOLVED FIXED
Bugzilla 2.20

People

(Reporter: caillon, Assigned: shane.h.w.travis)

References

(Blocks 5 open bugs)

Details

Attachments

(2 files, 8 obsolete files)

Originally, it was thought that the emailprefs table would also hold general prefs. The thinking has changed and we need a general prefs table. See bug 73665 for the emailprefs table. user prefs such as the one in bug 7710 would go here. The 'mybugslink' from profiles table might also want to get moved here. And any new user prefs should go here as well. Mark dependencies as needed.
*** Bug 95618 has been marked as a duplicate of this bug. ***
Some other prefs which could be useful... (copied from bug#95618): 1) turn on/off quips 2) enable/disable css 3) specify user css sheet 4) specify user inline style 4) specify preferred layout of bug activity (seperate/inline) 5) show simple/complex query page 6) preferred buglist column layout
I think each of those prefs should be filed as a separate bug if they aren't already and marked as dependent on this.
Most of that list are already bugs - I'll search out and update the dependencies soon.
Blocks: 41972
Blocks: 69654
Blocks: 11368
Blocks: 65391
Depends on: 16775
No longer depends on: 16775
Blocks: 16775
Blocks: 65386
What's wrong with the profiles table? None of those things seem to need multiple values.
First, the profiles table should be kept as small as possible with as few fields as possible since it used for authentication. (IMO, the profiles table is poorly named since it's primary function is user authentication.) Second, we should store things on a relational basis not based on which tables store multiple instances of something and which doesn't. Preferences and authentication information are different. Thirdly and probably the selling point, if we have a separate table we only have to store preferences for users that modify the default preferences, thus saving DB space. Were we to include it in the profiles table, everyone would have to have preferences stored be they set or not.
Blocks: 100390
This is now on the "we really want this for 2.16, but won't hold the release for it if it's not done by then" list.
Priority: -- → P1
Target Milestone: --- → Bugzilla 2.16
Blocks: 109692
Blocks: 108983
Blocks: 111497
Modifying summary to make this bug easier to find.
Summary: Need a general user prefs table → Need a general user preferences table
This obviously isn't going to make it in 2.16... -> 2.18
Target Milestone: Bugzilla 2.16 → Bugzilla 2.18
Darn. It's so necessary.
No longer blocks: 122900
Blocks: 63536
No longer blocks: 100390
Blocks: 67077
No longer blocks: 123352
No longer blocks: 111497
Blocks: 135595
Severity: normal → enhancement
Summary: Need a general user preferences table → Create a general user preferences table
OK, this table needs three columns... userid, preferencename, and value. The big question is what data type do we use for value? some preferences will be simple booleans. Others could potentially be pretty long (the mybugslink for example). On the other hand, mybugslink could and probably should become a named query instead.
I was thinking two tables: prefdefs and profileprefs -------- ------------ prefid userid name prefid default value This allows for a SQL query like this: SELECT IFNULL(profileprefs.value, prefdefs.default) etc. But I ran into the same problem, what type of field to use for prefdefs.default and profileprefs.value. Most seem to be boolean, but there are some that will require text, like bug 69654 which allows for some user defined CSS. I wonder if a tinytext would work? And maybe have a prefdefs.type that the cgi's could then interpret the correct value of default/value with?
When I was thinking about user prefs, I also wanted a prefdefs.type (boolean/string/url/list/...) which made things a lot easier when it came to displaying prefs and for specifying the GUI for editting prefs. Also, a prefdefs.desc may be needed (to explain the pref on the GUI)? I was wondering if it would be possible to combine this user prefs table with the email prefs in bug#73665? (And also what about the system params (bug#46296) -- could they use a prefdefs table to store system parameters maybe?)
> Also, a prefdefs.desc may be needed (to explain the pref on the GUI)? I think this will go away in feilddefs.desc in favor of a template that describes the fields (fielddefs has field-descs.html.tmpl that is not in full use yet). So I think we would just have a prefs-descs.html.tmpl.
Reassigning to me... WRT comment 13 > I was wondering if it would be possible to combine this user prefs table with > the email prefs in bug#73665? I vote on a seperate table. If we use the same table as the email prefs, then we will have to have a field for every pref. If we use a profile_prefs table, as I suggest, then only those values which are not the default are stored. > (And also what about the system params (bug#46296) -- could they use a prefdefs > table to store system parameters maybe?) I think this is possible, if the prefdefs table gets a system/user flag.
Assignee: myk → jeff.hedlund
Blocks: 175861
No longer blocks: 67077
No longer blocks: 175861
No longer blocks: 69654
Enhancements which don't currently have patches on them which are targetted at 2.18 are being retargetted to 2.20 because we're about to freeze for 2.18. Consideration will be taken for moving items back to 2.18 on a case-by-case basis (but is unlikely for enhancements)
Target Milestone: Bugzilla 2.18 → Bugzilla 2.20
Blocks: 199048
Bugzilla 2.20 feature set is now frozen as of 15 Sept 2004. Anything flagged enhancement that hasn't already landed is being pushed out. If this bug is otherwise ready to land, we'll handle it on a case-by-case basis, please set the blocking2.20 flag to '?' if you think it qualifies.
Target Milestone: Bugzilla 2.20 → Bugzilla 2.22
OK, I guess since I'm thinking about this, I might as well take it. Here are my thoughts, after some discussion with travis: We should just use the profiles table for the preferences. Then we only need a WHERE and no JOIN for most preferences lookups. After all, I don't plan any many-to-one or one-to-many relationships for preferences-to-profiles. Why have them in separate tables? Just like "a bug HAS a state", "a user HAS a preference." (I've implemented systems where a User and UserPrefs were separate objects, and it was sort of silly. You just have a User carrying around a UserPrefs object that he has to keep digging into. That would be similar to having a separate table.) As far as comment 6 goes, We're looking up on an index; table size doesn't matter for speed. The defaults won't take up significant space, even for thousands of users. Does the concern have something to do with a LOCK TABLE statement that can't be avoided somewhere (perhaps in the yet-unwritten code for this bug)? All we're doing in this bug is creating the table and the mechanism to modify it with. We should also implement ONE simple pref, so that we know the implementation works and is easy to code with. It will also encourage me to create a decent UI. How about implementing bug 41972 inside this bug? I think that would be pretty simple. For now, changing default preferences will require changing the schema. Future ------ + A "prefdefs" table that allows the admin to enable/disable the use of certain preferences. + Perhaps the ability for the admin to change the default preferences?
Assignee: jeff.hedlund → mkanat
(In reply to comment #18) > Here are my thoughts, after some discussion with travis: Here are my thoughts, after some more thought and discussion. > We should just use the profiles table for the preferences. Agreed wholeheartedly. > We should also implement ONE simple pref, so that we know the > implementation works and is easy to code with. ... How about > implementing bug 41972 inside this bug? Agree in principle, dislike conflating two different bugs. I think we should *test* this with a fix for bug 41972 (I agree that's a nice, simple one), but not try and check them in together. > For now, changing default preferences will require changing the schema. This is *not* going to fly, I know that already. As such, we need to implement at least one additional table -- the prefdef table. It can look like Jeff's in comment #12, because I think that's all we need for now. > Future > ------ > + A "prefdefs" table that allows the admin to enable/disable the use of > certain preferences. > + Perhaps the ability for the admin to change the default preferences? Already gotta have the prefdefs. Since that's the case, we definitely need an admin-userpref page as well as a plain old userpref page. The former would let the admin change default value for a given userpref. I'm torn on trying to do the disable in round one of this as well; might be conceptually easier to do it all at once, but I'd rather get it *in* and come back to it if it's going to cause us all sorts of troubles. This has sat for long enough already, and it already has eleven other bugs waiting for it to appear. :)
I had always imagined this happening with a new table. If we used 'profiles', would we not have to edit the schema each time we added a new user-pref? One things it would be nice to decide on now, and is independent of the schema change and implementation, is the interface to the routine used to check a user_pref, so that the blocked bugs could be worked on. Any proposals for that? What about the completely just made up, but probably not too surprising: my $user_pref_value = user_pref('pref_name',$user); where $user is optional and defaults to the logged in user if not specified. Is it userid, username or Bugzilla::User object? If the user is not logged in, then the system default is returned. If the user has not set a pref, then the system default is returned.
(In reply to comment #19) Yes, I've been thinking about it, and we'll use a separate prefs table, and a prefdefs table, to handle the defaults. (In reply to comment #20) Here's the interface I imagine: Bugzilla::User::get_pref(prefname) prefname; string: the short name of the preference to get (DB column name) The user is already defined, because we're calling this from an initialized user object. Bugzilla::User::set_pref(prefname, value) prefname; string: the short name of the preference to get (DB column name) value; scalar: The new value to set the preference to. In templates, I believe that we should just be able to do: user.get_pref.prefname or user.prefs.prefname Or something like that. justdave was telling me how to do it in IRC, but I forget.
Blocks: 26257
mkanat, travis, could you please already sumbit a "work in progress" patch? I'm interested to see the structure of your pref table and how to access it. I need it for bug 26257. ;) Thanks!
For the record, I would prefer to see something set up where the names of the preferences are not db columns, but a row in the table. Customization is my primary reasoning. If the preference names are db columns, then the schema has to be modified to add a preference. If a third-party plugin to Bugzilla comes along and needs to install a new user preference, they should be able to do so simply by adding a default for it in the defaults table and adding some UI to twiddle it via a template hook.
Alright, new schema idea. I think this one works, but I *really* want some feedback from people. This is going to work like the 'groups' subs in User.pm; when called, it will load a user's preferences into a hash table if they don't already exist, or do a simple hash lookup if they do. (An update to the hash table is forced if the admin changes a preference default, or the user changes prefs.) Three tables: 1) 'prefdefs' table. This stores the values germane to the preferences themselves. . pref_id : integer, unique (KEY) . pref_name : varchar (This is what is shown to admin and users) . default_pref_val : int (Default value if no individual pref is set) . enabled : int (0=Off, 1=On) If a given pref_id is disabled, then the default_pref_val will *always* be used. The UI for changing user prefs will show the pref_name and the default value, and will indicate that it is locked. (Alternately: it won't show anything at all, so the user won't even know that the option exists. Thoughts?) 2) 'userprefs' table. Stores the preferences of individual users . user_id : from profiles. (COMBINED KEY) . pref_id : from prefdefs (COMBINED KEY) . pref_val : int * If the user_id/pref_id key does not produce a row, the prefdefs.default for that pref_id is loaded into the hash. * If a pref is disabled after a user has entered a value, the row is not deleted in case the preference is enabled again in the future... it simply is never looked up. 3) 'prefvals' table. Stores the meaning of any given preference value . pref_id : from prefdefs (COMBINED KEY) . pref_val : int (COMBINED KEY) . display : varchar(100) (displayed text for this value) When defining a new preference, the Admin will make entries into this table to define what the legal values are. These can be as simple as 0=off, 1=on (which will be offered to the Admin as a default when creating a new userpref), or complex (0="magenta and mauve", 1="passionfruit, pink, and peach", etc.) Aside: anyone got a better column name than 'display'? I'm sure one exists, but couldn't come up with one... In the to-be-created userprefs.cgi?tab=individual-preferences UI, users will be presented with a series of lines with the following information, one per existing (or enabled) userpref: prefdefs.pref_name [drop-down with prefval combos] or [X] Use system default (set to: default_pref_val) Things will be disabled/not shown as appropriate if prefdef.enabled = 0. I'm a little jammed on creating the Admin UI; should we allow them to edit the meaning of prefvals.display after it is entered? Do we let the admin to remove a given pref_id or pref_val if people have already chosen them? If so, do we just delete the corresponding entries in userprefs? These can be determined later... and I think Max is doing the UI anyway. (Max, confirm?) Anyway, I *think* that this is an optimal schema solution... but I wouldn't be surprised if there's a flaw somewhere or some facet that I hadn't considered. Feedback, please!
(In reply to comment #24) > I'm a little jammed on creating the Admin UI; should we allow them to edit > the meaning of prefvals.display after it is entered? Do we let the admin to > remove a given pref_id or pref_val if people have already chosen them? Okay, ignore these ravings. For some reason I was thinking that non-code- hacking admins would/should be able to create their own custom userprefs from their admin page... when in fact that is not the case any more than it is with parameters. Admins will be presented with a list of all existing userprefs, allowed to choose a default for their site (which will be saved in the database) and allowed to enable/disable a userpref completely if he does not want users to have access to anything *but* the default value. No entry/edit capabilities are needed... just drop-down boxes, an enable/disable checkbox, and a Commit button. D'oh!
(In reply to comment #24) > Three tables: Fine! I also have three. ;) > 1) 'prefdefs' table. This stores the values germane to the preferences > themselves. > > . pref_id : integer, unique (KEY) > . pref_name : varchar (This is what is shown to admin and users) > . default_pref_val : int (Default value if no individual pref is set) > . enabled : int (0=Off, 1=On) I would suggest the following column names, for clarity: . name : VARCHAR(20) PRIMARY KEY [short name of the pref, without spaces] . desc : VARCHAR(200) [Pref full name / description displayed to users] . default : SMALLINT [Default value if no individual pref is set] . enabled : TINYINT [If 0 (= off), the 'default' value is forced to users] 'default' must be a valid 'val' values from defvals! Instead of an integer as a primary key, I suggest here a string. Or at least, if you *absolutely* want an integer as a primary key, you should add a 'descr' field which will be displayed to users. So we could have: ================================================================= prefdefs ================================================================= id name desc default enabled ================================================================= 1 security "Security Level" 1 0 2 bug_order "Bugs in buglists ordered by" 1 1 ================================================================= ================================================================= prefvals ================================================================= pref_id val desc ================================================================= 1 0 "No security: accept all changes" 1 1 "Do not accept changes submitted from outside Bugzilla" 2 0 "Bug Name" 2 1 "Bug ID" 2 2 "Creation Date" 2 3 "Status" ================================================================= So you don't need to remember which pref has which ID, you can call it using it's (short) name and still be able to display a more friendly full name / description of the pref to users. > 2) 'userprefs' table. Stores the preferences of individual users > > . user_id : from profiles. (COMBINED KEY) > . pref_id : from prefdefs (COMBINED KEY) > . pref_val : int OK here, with pref_val referencing a valid 'val' in defvals. > * If the user_id/pref_id key does not produce a row, the prefdefs.default > for that pref_id is loaded into the hash. OK! > * If a pref is disabled after a user has entered a value, the row is not > deleted in case the preference is enabled again in the future... it > simply is never looked up. Clever! ;) I like it. > 3) 'prefvals' table. Stores the meaning of any given preference value > > . pref_id : from prefdefs (COMBINED KEY) > . pref_val : int (COMBINED KEY) > . display : varchar(100) (displayed text for this value) To be consistent with what I have already said: . pref_name : VARCHAR(20) [Name of the pref we are pointing to] . val : SMALLINT [ID of this option] . desc : VARCHAR(200) [Full name / description of the option displayed] Here pref_name references 'name' in prefdefs. Again, if you prefer an integer as a primary key, I fully agree with your solution. > anyone got a better column name than 'display'? 'desc' (description)?
(In reply to comment #26) > I would suggest the following column names, for clarity: > . name : VARCHAR(20) PRIMARY KEY [short name of the pref, without spaces] > . desc : VARCHAR(200) [Pref full name / description displayed to users] > . default : SMALLINT [Default value if no individual pref is set] > . enabled : TINYINT [If 0 (= off), the 'default' value is forced to users] > > 'default' must be a valid 'val' values from defvals! > > Instead of an integer as a primary key, I suggest here a string. Or at least, if > you *absolutely* want an integer as a primary key, you should add a 'descr' > field which will be displayed to users. I think the Primary Key should be an integer - this gives you the scope to rename the prefrence for clarity without breaking the references to it. I was informed on #webtools tonight that this was the reason that products are now referred to as id's and not names as far as I could gather.
(In reply to comment #27) > I think the Primary Key should be an integer - this gives you the scope to > rename the prefrence for clarity without breaking the references to it. But I defined *two* names here: a short one which should never change and a long one (the one displayed to users) which can be change. But I know some of you prefer integers as primary keys in any case. ;)
OK, as far as customization goes, I agree with justdave. I suppose we should use table rows. For the record, here's what I don't like about table rows: * We lose the built-in typing of MySQL. * It's more difficult to enforce "Um, don't delete that row," than "don't delete that column," and more difficult for checksetup to figure out the validity of the prefdefs and profile_prefs table. For the purposes of customization, the Primary Key for the prefdefs table should be a varchar. This keeps the PK for a custom pref the same between multiple instances of Bugzilla, whereas otherwise it would be (most likely) randomly assigned as an auto_increment. We use "isactive" as a colum name in the BZ schema, instead of "enabled." If we have a prefvals table, the "prefvals" column in profile_prefs must be a string (varchar) -- some preferences allow freeform text to be entered. This brings me to the problem of how large that varchar should be... Also, once we start using a varchar, we lose the ability to easily rename the pref value... Ah! I know how we'll do it. :-) There will be a column called preftext that's read in as the prefvalue if the prefval is NULL. That will be a bit difficult in Bugzilla::User::set_pref, but that's OK; I can deal with that. :-)
(In reply to comment #29) > For the record, here's what I don't like about table rows: > * We lose the built-in typing of MySQL. Do we *need* this any more? That's part of what I came to realize; we can't use ENUMS (because they're unique to MySQL and thus break other DBs) so we were going to have to store any text strings as references anyway. This does prevent us from using numbers *quite* so easily... but how many preferences are going to be numbers? Certainly none of the bugs that are blocked by this one... > * It's more difficult to enforce "Um, don't delete that row," than "don't > delete that column," True, but the only deletions that should be happening are in the code itself. That someone can directly access the DB and completely FUBAR themselves has always been a danger, and not one that code is usually written to minimize. > and more difficult for checksetup to figure out the validity of > the prefdefs and profile_prefs table. True, I'll give you that one... at least to a small degree. > For the purposes of customization, the Primary Key for the prefdefs table > should be a varchar. Sounds congruent to Frederic's comment #26. I guess I can see that ... WHERE prefdefs.pref_id = 'quips_pref' is a lot more descriptive and human-readable than ... WHERE prefdefs.pref_id = 17 > We use "isactive" as a colum name in the BZ schema, instead of "enabled." Aaah, I *knew* there was a better word. Consider it accepted. > If we have a prefvals table, the "prefvals" column in profile_prefs must be a > string (varchar) -- some preferences allow freeform text to be entered. Several comments: 1) wth is profiles_prefs? You talking about the userprefs table? 2) No preferences allow freeform text to be entered. They're all choices from a drop-down list... at least for this iteration. Only one of the blocked bugs (bug 109692) needs anything more than that for now, and that can still be settled by a series of discrete choices -- checkboxes, perhaps. Remember; we're building a foundation, not the 14th floor. If someone else needs freeform text boxes, they can add them then. That renders the rest of your comment moot, except for this one: > ... but that's OK; I can deal with that. :-) You and I need to talk about division of labour. I wanted to get a start on this over my Christmas break, and I thought you didn't have time. (I had even thought of reassigning to me, based on IRC conversations.) Has time freed up for you?
Blocks: 72118
The human readable descriptions should come from a template so they can be localized. Putting them in the database makes it unlocalizable.
so we have to drop the 'desc' column in both tables.
Assignee: mkanat → travis
Final schema, I think, incorporating Frederic's suggestion of a string key, Max's rename of 'enabled', and Dave's contention that the user-display part needs to be in a template rather than a database for easier localization. ================== 1) 'prefdef' table. . pref_name : varchar, unique (KEY) . default_value : varchar . isactive : int (0=no, 1=yes) This stores the master list of all possible user preferences. It also stores one default value, which is an index into the template-replacement scheme (as Dave suggested, so that it's easier to localize). If a user does not set a preference, this value will be used. Finally, 'isactive' indicates whether or not the user even has an option to set their own preference. If this is set to 'no', the user will not even see the preference option; it will be as though the option does not exist as far as the user is concerned. if isactive=0, everyone uses the default. 2) 'prefval' table. . pref_name : varchar (COMPOUND KEY) . value : varchar (COMPOUND KEY) This stores the values germane to the preferences themselves. - prefval.pref_name must exist in prefdef.name - Each name/value combination must be unique This scheme allows us to re-use values, so we don't have to have quips_on/quips_off, borders_on/borders_off, etc. We define a preference named 'quips' in prefdefs, and indicate what its acceptable values are in this table. Again, value is an index into the template-replacement scheme mentioned above. 3) 'userprefs' table. Stores the preferences of individual users . user_id : from profiles. (COMPOUND KEY) . pref_name : as from prefval (COMPOUND KEY) . pref_val : as from prefval This table stores a user's individual preferences, if they have ever been set. The combination of user_id/pref_name must be unique; the combination of pref_name/pref_val must appear in the prefval table. * If a pref is enabled, the pref-load algorithm looks in this table to try and find a value for the given user_id/pref_name combination. If no row is produced, the prefdef.default for that pref_name is loaded into the hash. * If a pref is disabled by the admin after a user has entered a value, the row is not deleted in case the preference is enabled again in the future... it simply is never looked up by the pref-load algorithm. * UI will give the user an option to delete their stored value and use the admin-set default instead.
Status: NEW → ASSIGNED
My 2 comments: 1) What about if we need to store a value such as an URL, or the buglist sort order, or buglist column order, or some other less constrained value for a user pref? 2) The DB design would look better to me with a numeric pref_id in the 'prefdef' table which was used to do the SQL joins. The pref_name would still be needed in the 'prefdef' table, and used in the code for pref lookup, but the SQL joins would use the numeric pref_id (in my world, anyway :)
(In reply to comment #34) > My 2 comments: > 1) What about if we need to store a value such as an URL, or the buglist sort > order, or buglist column order, or some other less constrained value for a user > pref? I answered that objection already, in comment #30; see the response to 'freeform text'. (Do people not read through the historical comments on a bug before responding? This isn't aimed at you specifically, Gavin... you're not the only, or even the first, person who has asked me questions I've already answered.) Right now, there is *no* functionality for user prefs. I'm building *some* functionality for user prefs - enough to satisfy most of our current needs. It is not reasonable to expect the ultimate, absolute, be-all end-all solution to a problem *as a first step*. What you're asking for is a reasonable extension, but it is not a critical initial component. Remember, folks... admins can't just create new user prefs for their users; like parameters, they are defined by programmers. Right now, nothing *needs* a freeform box, so I'm not going to go to extra work to incorporate one. Max and I have discussed it, and this schema is extensible to accommodate the need should it arise (perhaps with the need for one more table), so that's good enough for me. > 2) The DB design would look better to me ... ... and it wouldn't to me. Since I'm coding it, I win. :) (Again, read back please. Much of this discussion has already taken place.) Integers for the sake of integers ... why? The point is to have a unique way of indexing into the table, which is provided by the name (never changes, not visible to the user) and the value (never changes, not visible to the user). Both of these things are translated into visible strings by pulling values from a template, for easier localization... so the only people who will need to see the actual table values are programmers. Look at the following two examples: if (UserPref(17) = 2) vs. if (UserPref(comment_sort_order) = "oldest_to_newest") Which one can be read by a human? Which one is going to be easier to program, and will require fewer references back to the table to determine whether comment_sort_order was 17 or 16? Which is (therefore) going to be easier to debug and maintain? (Very important considerations on a project like this.) I know where I stand. (And for the record, it's not where I stood when I started this debate.)
No longer blocks: 16775
Blocks: 36013
Minor modification to schema, and rationale: 2) 'prefval' table. . pref_name : varchar (COMPOUND KEY) . value : varchar (COMPOUND KEY) . sortorder : integer unique(pref_name, value) unique(pref_name, sortorder) The one good thing about integers is that you can select by them and order them easily... words, on the other hand, don't sort so well. This would be a pain when it comes to displaying the information to the user, as a programmer would have to be very creative when creating values to ensure that they got displayed in the order he/she wanted... either that, or go with something ugly like 1- on/2-off/3-random. Either one destroys the whole string-reusability aspect. Adding a .sortorder column so the order in which the various choices are presented to the user can be easily and explicitly defined by the programmer at the time of the creation of a new User Pref.
Blocks: 61246
Attached patch Code patch for tip (obsolete) (deleted) — Splinter Review
First of all... thanks tons to Max for the help with this. Without his insights, or time as a sounding board, this patch wouldn't be nearly as good as it is. Secondly, a little explanation. I have changed the name from 'User Preferences' to 'Settings'. This was necessitated by the fact that there is already a User Preferences page with multiple tabs on it, and this needed to go there, so it needed a different name. There exist three ways that Bugzilla can be controlled/customized: 1) Parameters: Admin has complete control over them for the site as a whole. 2) User Prefs: things like email, stored queries, etc. Completely controlled by the user on an individual level. This patch creates a third method -- Settings -- which fall midway between the two. Each setting has a 'global default' which will apply to people who are not logged in, and to people who have not chosen to override it. This global default can be changed by the Admin -- like a Parameter -- and the new setting will immediately affect every user on the site... *except* those who have chosen to override the global setting with their own personal preference. Furthermore, if an Admin decides to, a Setting can be completely disabled; this removes it from the Users' display, and the Global Setting will then apply to all users whether or not they have ever chosen a personal value for that setting. (In essence, disabling a Setting turns it into a Parameter.) Any other questions, I'll be happy to answer. I asked Max for review because he understands how it works already, but I'll gladly accept code/usability reviews from anyone.
Attachment #173917 - Flags: review?(mkanat)
Here is some test data for people if they wanted to use it. Two files; one SQL file that adds three settings to the database, and a diff file that adds the necessary information to the (new) file settings-desc.none.tmpl
Whiteboard: patch awaiting review
Target Milestone: Bugzilla 2.22 → Bugzilla 2.20
Attachment #173917 - Flags: review?(jouni)
Forgot to add this dependency. This v1 patch should apply just fine, but it needs the latest patch for bug 281574 to be applied before it will compile or execute.
Depends on: 281574
Comment on attachment 173917 [details] [diff] [review] Code patch for tip >diff -Naur --exclude=CVS --exclude=data --exclude=.htaccess --exclude=localconfig --exclude=skins tip/Bugzilla/User/Setting.pm tip.userprefs/Bugzilla/User/Setting.pm >--- tip/Bugzilla/User/Setting.pm 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/Bugzilla/User/Setting.pm 2005-02-09 15:26:31.251790429 -0600 I realized this later in the review (so I can't put the comment right below the EXPORT statement), but I think that we shouldn't EXPORT the functions that are only used by Bugzilla::User. We should just EXPORT the functions that are used by CGIs. The functions used by Bugzilla::User can be called by their full name. >+# The Initial Developer of the Original Code is Netscape Communications >+# Corporation. Portions created by Netscape are Actually, you are the original developer. :-) I usually take this section *out* of new files for Bugzilla, but I'm not sure if that's actually the correct thing to do. >+use Safe; You must have patterned this off of Bugzilla::Config. :-) "Safe" isn't necessary, for this code. >+ # create a blank $self >+ my $self = { >+ 'is_enabled' => 0, >+ 'legal_values' => [], >+ 'default_value' => "", >+ 'value' => "", >+ 'is_default' => 0, >+ }; Hrm... should we actually set these values here? I think that we should just leave them blank, and then if they're not filled-in somewhere, we'll get errors (like we should). I don't think creation of a blank Setting should be allowed, yet. >+ # If there's nothing left after taking out the setting and ID >+ # then we need to retrieve the information ourselves. >+ if (scalar @_ == 0) { I know that I suggested this, but either this method of creating the Setting or the other one might not actually be used in the current code, right? I generally prefer to remove dead code, now that I've thought about it. If one of the methods isn't used, it shouldn't yet be allowed. >+ my $sth = $dbh->prepare(q{SELECT default_value, is_enabled, setting_value >+ FROM setting The SQL shouldn't have newlines in it. See bug 139559. >+ LEFT JOIN profile_setting >+ ON setting.name = profile_setting.setting_name >+ WHERE name = ? >+ AND (profile_setting.user_id = ? >+ OR profile_setting.user_id IS NULL)} ); >+ $sth->execute($setting_name, $user_id); >+ my ($default, $is_enabled, $value) = $sth->fetchrow_array(); Nit: If that all returns only one row, it can be a selectrow_arrayref. See the DBI docs at: http://search.cpan.org/~timb/DBI-1.46/DBI.pm >+ if ( ($is_enabled) && (defined $value) ) { Nit: You don't need the parens around those inner statements. >+ } >+ else { >+ # If the values were passed in, simply assign them and return. This is the "other method" I was talking about. Are they actually both used? >+##################################################################### >+########################## Subroutines ########################## >+##################################################################### The standard Bugzilla format for a header like that is: >##################################################################### ># Subroutines >##################################################################### >+ >+sub GetDefaultSettings { If this is public and exported, it needs to be in lowercase_with_underscores. If it's private (not to be used by code outside of this package) it needs to _start_with_an_underscore. Same for all of the other functions. It's standard perl module style, which we are striving for. >+ my $sth = $dbh->prepare(q{SELECT name, default_value, is_enabled >+ FROM setting >+ ORDER BY name}); Newlines again. Sorry I didn't comment on this before -- I wasn't really thinking about it. I won't comment on the other ones -- they just need a quick fixing (to be a series of joined strings). >+ $sth->execute(); >+ while (my ($name, $default_value, $is_enabled) = $sth->fetchrow_array()) { >+ $default_settings->{$name} = new Bugzilla::User::Setting >+ ($name, 0, $is_enabled, GetLegalValues($name), The normal Bugzilla style is to have the paren next to the function call itself. So it would be: $default_settings->{$name} = new Bugzilla::User::Setting( >+ if ( ($is_enabled) && (defined $value) ) { Nit: Parens again. perlstyle (the man page that we follow) actually says to avoid too many parens. So that's what we do. :-) >+ $settings->{$name} = new Bugzilla::User::Setting >+ ($name, $user_id, $is_enabled, GetLegalValues($name), And the paren-location, again. >--- tip/Bugzilla/User.pm 2005-02-09 00:36:43.000000000 -0600 >+++ tip.userprefs/Bugzilla/User.pm 2005-02-09 17:30:01.525070459 -0600 > [snip] >+sub settings { > [snip] >+ if ($self->id) { >+ $self->{'settings'} = GetUserSettings($self->id); >+ } else { >+ $self->{'settings'} = GetDefaultSettings(); >+ } Does that actually work for a user who isn't logged-in? Normally in the other functions, I think that we're returning an empty object, instead of >+sub delete_user_setting{ This is going to be called as Bugzilla->user->delete_user_setting(), so I think the "user" in the name of the function is redundant. >+ my ($self, $user_id, $setting_name) = @_; >+ >+ $self->settings; You don't actually need to do that $self->settings, there. >+ $self->{'settings'}->{$setting_name}->{'is_default'} = 1; >+ $self->{'settings'}->{$setting_name}->{'value'} >+ = $self->{'settings'}->{$setting_name}->{'default_value'}; These two lines should be: $self->settings->{$setting_name}->{'is_default'} = 1; $self->settings->{$setting_name}->{'value'} = $self->settings->{$setting_name}->{'default_value'}; You'll have to note that they're there to deal with the fact that the settings object could have already been loaded, and we need to update it if that's the case. >+sub set_user_setting { My comment about the "user" in this sub name applies here, too. >+ $self->{'settings'} = GetUserSettings($self->id); You don't need that call at all, there. >+ if ($self->{'settings'}->{$setting_name}->{'is_default'}) { Should be $self->settings->{$setting_name}->{'is_default'}. ALL the other calls to $self->settings should be this way. (I won't add comments to the rest of them.) >@@ -1100,6 +1163,32 @@ >+=item C<settings> Cool. This is well-documented. :-) >+# user pref system >+ >+$table{setting} = >+ 'name varchar(32) not null primary key, >+ default_value varchar(32) not null, >+ is_enabled tinyint not null default 1'; I usually do tinyint(1), there, I think, for booleans. >+ >+$table{setting_value} = >+ 'name varchar(32) not null, >+ value varchar(32) not null, >+ sortindex tinyint, not null, sortindex should actually be a smallint, like it is for target_milestones. >--- tip/editsettings.cgi 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/editsettings.cgi 2005-02-09 16:22:09.523894160 -0600 >+ >+require "CGI.pl"; >+ >+# Use global template variables. >+use vars qw($template $vars); I have come to realize only recently that you don't need that use vars statement. Instead, you can have: my $template = Bugzilla->template; my $vars; Which is better. You might need to make that "our $vars" since you modify $vars in a subroutine, though. But I think that it's possible that you wouldn't need CGI.pl then, either. >+sub SaveSettings{ > [snip] >+ >+ if ($value =~ /^(\w+)$/ ) { >+ $value = $1; >+ } What's that check for? Is it just for taint purposes? >+print Bugzilla->cgi->header; Nit: You could do the my $cgi above this, and then use that. >+my $cgi = Bugzilla->cgi; >+my $action = trim($cgi->param('action') || 'load'); Usually we just leave the action empty, in this case, and just have a choice for what happens when the action is empty. I suppose I don't mind this way of doing it here, so much, though. >--- tip/template/en/default/admin/settings/edit.html.tmpl 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/template/en/default/admin/settings/edit.html.tmpl 2005-02-09 17:15:13.974798651 -0600 [snip] >+ <table border cellpadding="4"> In HTML 4.0, every attribute must have a value. So that would be border="1" if that's what you want. If it's not what you want, it would be border="0". [userprefs.cgi] >+ elsif (defined $cgi->param("${name}") ) { >+ my $value = $cgi->param("${name}"); Nit: You actually don't need the quotes and brackets, there. Overall, this patch looks really great and clean. Just a few tiny little bits here and there to fix up. The overall design is excellent, and the code is very elegant. :-) I didn't look over the UI very hard -- I'm going to let Jouni do that.
Attachment #173917 - Flags: review?(mkanat) → review-
(In reply to comment #40) > Actually, you are the original developer. Yup, realized this myself later; have already taken it out of the current version. > >+ # create a blank $self > Hrm... should we actually set these values here? I think that we should > just leave them blank, and then if they're not filled-in somewhere, we'll get > errors (like we should). Been wondering about that myself... but I noticed it being done in either the User or Bug code (I forget which) so I followed that style. > If one of > the methods isn't used, it shouldn't yet be allowed. I understand the logic of 'removing unused code', but in this case I disagree. The constructor for this object shouldn't *only* depend on other subroutines to dig up the values for it; it should have some way of populating itself if those values aren't passed in. Someone wanting to use a single Setting in the future (as opposed to hash full of them) shouldn't also have to write the constructor for this object just to get that functionality. For one thing, they won't know the code, and I do... so why not put it in now for completeness' sake? > The SQL shouldn't have newlines in it. See bug 139559. I see an old bug with no discussion or consensus, and I don't recall seeing any other reviews mentioning it or adhering to it. Why here? Why now? I think it's worth getting some input from Dave on whether or not that's bug is/is going to become canon; if it is, it's definitely worthy of an update to the Developer's Guide since a lot of old code (and a lot of new code, even) is not following it. > >+ my ($default, $is_enabled, $value) = $sth->fetchrow_array(); > Nit: If that all returns only one row, it can be a selectrow_arrayref. See > the DBI docs at: http://search.cpan.org/~timb/DBI-1.46/DBI.pm I looked at that when you first suggested it. I need three distinct pieces of data; why would I want to use a call that returns a hash reference? > The standard Bugzilla format for a header like that is: > >##################################################################### > ># Subroutines > >##################################################################### I see it done four different places in four different ways. How does that make this way the standard? (Don't get me wrong, if there *is* a defined standard with a reason for being, I'll likely follow it... I just haven't seen any evidence of one.) > If this is public and exported, it needs to be in lowercase_with_underscores. > If it's private (not to be used by code outside of this package) it needs to > _start_with_an_underscore. Same for all of the other functions. It's standard > perl module style, which we are striving for. Reference please - bug # or perl docs or perl books - so I can look it up and know for next time? > Does that actually work for a user who isn't logged-in? Did when I tried it... I think. Pretty sure I tried it, anyway; 85% or so. > Normally in the other > functions, I think that we're returning an empty object, instead of ... instead of what? Don't leave me hanging here, man! :) If we return nothing for a non-logged-in user, then the very next thing the calling code would have to do (on realizing that User->setting->{$name}-> {'value'} has nothing in it) is perform some sort of call to return the default value, because code that uses Settings will need to know what the global value for this setting is. My thought process was; why make the code make two different calls? Why not just return the value regardless, since it has to have *something* before it can continue anyway? > I usually do tinyint(1), there, I think, for booleans. There are exactly two instances of tinyint(1) being used for booleans in checksetup.pl, vice dozens - literally - that aren't. Also, according to several comments in the MySQL documentation, tinyint(1) causes problems with MySQL connector, and furthermore doesn't even modify the storage size in some (older) versions of MySQL. I guess what I'm asking is... do we really care? This a nit, or a show-stopper? > sortindex should actually be a smallint, like it is for target_milestones. I would argue the other way around - that the sort index in target_milestones should actually have been a tinyint. Given that moth Milestones and Settings are converted to pull-down lists, nobody is going to get anywhere *near* 255 values (or even 127 if it's signed - I can't remember) and have it remain usable. > What's that check for? Is it just for taint purposes? Yes. Got a better way to do it? Or just suggesting that I need a comment to that effect? > >+ <table border cellpadding="4"> > In HTML 4.0, every attribute must have a value. So that would be border="1" > if that's what you want. If it's not what you want, it would be border="0". Stole this directly from show_activity.cgi, as I wanted what it had... so prolly needs fixing there too. Thx for the review!
(In reply to comment #41) > > >+ # create a blank $self > > Hrm... should we actually set these values here? I think that we should > > just leave them blank, and then if they're not filled-in somewhere, we'll get > > errors (like we should). > > Been wondering about that myself... but I noticed it being done in either the > User or Bug code (I forget which) so I followed that style. OK. Let's try not doing it here, and see how it goes, I think. > I understand the logic of 'removing unused code', but in this case I disagree. > [snip] OK, I agree with your logic. :-) > > The SQL shouldn't have newlines in it. See bug 139559. > > I see an old bug with no discussion or consensus, and I don't recall seeing any > other reviews mentioning it or adhering to it. Why here? Why now? I think it's > worth getting some input from Dave on whether or not that's bug is/is going to > become canon; if it is, it's definitely worthy of an update to the Developer's > Guide since a lot of old code (and a lot of new code, even) is not following it. It's not that hard to do. I've done it, in all my recent patches. It's true, there's definitely some code that doesn't do it, but if we can make life easier for people using administrative tools for relatively little effort, I don't see the downside. > > >+ my ($default, $is_enabled, $value) = $sth->fetchrow_array(); > > Nit: If that all returns only one row, it can be a selectrow_arrayref. See > > the DBI docs at: http://search.cpan.org/~timb/DBI-1.46/DBI.pm > > I looked at that when you first suggested it. I need three distinct pieces of > data; why would I want to use a call that returns a hash reference? It doesn't return a hash reference. selectrow_arrayref. > > The standard Bugzilla format for a header like that is: > > >##################################################################### > > ># Subroutines > > >##################################################################### > > I see it done four different places in four different ways. How does that make > this way the standard? It's most commonly been done this way in modern patches. I'd like to keep it that way. Most of the modules have it in that way, although it's true that some older modules have it differently. > Reference please - bug # or perl docs or perl books - so I can look it up and > know for next time? from "man perlstyle": "While short identifiers like $gotit are probably ok, use under- scores to separate words. It is generally easier to read $var_names_like_this than $VarNamesLikeThis, especially for non- native speakers of English. It’s also a simple rule that works consistently with VAR_NAMES_LIKE_THIS." > > Does that actually work for a user who isn't logged-in? > > Did when I tried it... I think. Pretty sure I tried it, anyway; 85% or so. OK. We're probably OK, then. :-) > > Normally in the other > > functions, I think that we're returning an empty object, instead of > > ... instead of what? Don't leave me hanging here, man! :) > [snip] Hahahaha. Sorry, I got caught up in other parts of the review and I must have just forgotten. Yeah, I agree with the way that you're returning it, actually. That's probably why I left off on the comment and didn't finish it. :-) > There are exactly two instances of tinyint(1) being used for booleans in > checksetup.pl, vice dozens - literally - that aren't. Also, according to > several comments in the MySQL documentation, tinyint(1) causes problems with > MySQL connector, and furthermore doesn't even modify the storage size in some > (older) versions of MySQL. OK. If it causes problems with some software, it's probably not a problem. It's just a nice thing to explicitly state what the thing should be. Eventually, when we have the cross-DB schema patch, all of these things will become normalized. I'm just sort of trying to normalize them, now. It doesn't particularly matter one way or another, I suppose. :-) > I would argue the other way around - that the sort index in target_milestones > should actually have been a tinyint. Given that moth Milestones and Settings > are converted to pull-down lists, nobody is going to get anywhere *near* 255 > values (or even 127 if it's signed - I can't remember) and have it remain > usable. Actually, it's fairly useful to have a sortindex that's a smallint. It helps for creating values like 100, 200, 300, 400, in case you plan to possibly put a lot of values between them in the future and you don't know how they're going to sort. All the sortindexes in the enums patch are smallints, too. > > What's that check for? Is it just for taint purposes? > > Yes. Got a better way to do it? Or just suggesting that I need a comment to > that effect? No, just a comment would be fine! :-) Will \w+ approve of numbers, too? I forget. > > >+ <table border cellpadding="4"> > > In HTML 4.0, every attribute must have a value. So that would be border="1" > > if that's what you want. If it's not what you want, it would be border="0". > > Stole this directly from show_activity.cgi, as I wanted what it had... so > prolly needs fixing there too. OK. :-) > Thx for the review! Hey, no problem! Thanks for writing the code! It's great. :-)
(In reply to comment #40) > I think that we shouldn't EXPORT the functions that > are only used by Bugzilla::User. We should just EXPORT the functions that are > used by CGIs. The functions used by Bugzilla::User can be called by their full > name. Forgot to comment on this in my initial response. What you're saying is diametrically opposed to what Dave said to me in IRC, and I agree with him. The only thing that should be using Setting is User; everything else that wants setting information should be making calls to User to get this info. For the most part, that's just what happens; the lone exception to this is editsetting.cgi, and only there because it needs to access Setting directly for admin purposes. Since that *is* an exception, and we don't want people mucking with that stuff as a general rule, I don't export those admin subroutines, and they must be called by their full name. OTOH, the subs that User needs *are* exported... because User is the only place that should have the "use Bugzilla::User::Setting;" directive, and the Setting subs should really be part of the overall User namespace. That't the logic behind having it this way, and it makes a lot of sense to me. If I'm missing something, please point out where.
Comment on attachment 173917 [details] [diff] [review] Code patch for tip A few general notes: You seem to be exceeding 80 chars for line length in quite a few places. I'm not going demand you to wrap all the lines, but please do pay attention and attempt to keep at max ~78 chars for lines that absolutely don't need the extra width. Re discussion on IRC, I support having syntactic sugar for accessing prefs: Somethign like User->setting('foo') would be great. The patch crashes when going to userprefs.cgi with no setting entries in the DB. Either we ship with some, or we fix this so it won't crash. The error is "Can't use an undefined value as a HASH reference at /var/www/bugzilla/tip/userprefs.cgi line 257." The patch doesn't pass the testing suite; fix this before next iteration. Onto the code, UI notes embedded: >--- tip/Bugzilla/User/Setting.pm 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/Bugzilla/User/Setting.pm 2005-02-09 15:26:31.251790429 -0600 >+# bless ($self, $class); Nit: Why is this here, commented out? >+sub GetDefaultSettings { >+sub SetDefaultSetting For some reason, I find "Default Settings" particularly clumsy in this context. I could quite well go with "GetDefaults" and "SetDefault" (or SetDefaultValue perhaps), but this is naturally no showstopper. >+ $settings->{$name} = new Bugzilla::User::Setting >+ ($name, $user_id, $is_enabled, GetLegalValues($name), >+ $default_value, $value, $is_default); I'm slightly afraid of the perf impact the continous GetLegalValues calls are going to cause. Continously retrieving practically never-changing data feels like an awkward design decision. Although premature optimization blows, I still strongly think we should avoid adding payload to User object construction. Versioncache is evil, but it does come to mind. The other alternative might be to just get the alternatives when we need them (prefs page?). User.pm: ======== FWIW, and obviously a nit, I think the following code is clumsy: >+ return $self->{'settings'} if defined $self->{'settings'}; >+ >+ # IF the user is logged in >+ # THEN get the user's settings >+ # ELSE get default settings >+ if ($self->id) { >+ $self->{'settings'} = GetUserSettings($self->id); >+ } else { >+ $self->{'settings'} = GetDefaultSettings(); >+ } >+ >+ return $self->{'settings'}; I'd find it much more elegant with structure like the following: -- if (!defined $self->{'settings'}) { # If logged in, get user's settings; else use the defaults $self->{'settings'} = ($self->id) ? GetUserSettings($self->id) : GetDefaultSettings(); } return $self->{'settings'}; -- Note the absence of two returns and the clarified structure of the if block. Just my $0.02. >+sub delete_user_setting{ Nit: Space before { >+Returns a hash of hashes which holds the user's Settings. The first key is Unless there's particular reason to capitalize Setting, don't do it. You're using very mixed case anyway; I think all lower will suffice here. >+is_enabled - whether or not the user is allowed to create a preference >+ for themselves or must accept the global site default value Slightly unclear for me. Perhaps: "true if the user is allowed to set the preference themselves; false to force the site defaults". >+legal_values - an array of all allowable values for this setting I'd say "allowed", but this is nitpicking. >+value - the value of this setting for this user. Guaranteed to be >+ the same as default_value if the user is not logged in, >+ or if is_default is true; otherwise, could be any value >+ found in legal_values. >+is_default - a boolean to indicate whether the user has chosen to make >+ a preference for themself or use the site default. Nit: The additional lines of value and is_default documentation are indented by one space too many. Checksetup.pl ============= >+$table{setting_value} = >+ 'name varchar(32) not null, >+ value varchar(32) not null, >+ sortindex tinyint, not null, That comma over there crashed my checksetup (invalid SQL Syntax). Running MySQL 4.0.23. The UI, some generic comments: I don't find the way the prefs page works quite optimal. First, it looks fairly complex (it isn't that difficult a thing, really!). Second, the way the "rely on default" option works isn't very intuitive. My recommendations are: 1) Dramatically reduce the amount of explanatory text (I'll give better pointers later) 2) Waste the "Use default" and "Default value" columns, and rather integrate them into a new combo box choice (f.e. ("Site default (On)")). Unless you do this, you'll have to write JavaScript to block useless controls; I refuse to accept an UI where you can check "Use default" and then make totally irrelevant choices in the "Current value", which will then be nuked on submission. ;-) So what I'd like to see is options like Use quips at the top of bug list? [ |v] | Site default (No) | | Yes | | No | | Randomly | +--------------------+ >+++ tip.userprefs/template/en/default/account/prefs/settings.html.tmpl 2005-02-09 17:14:37.022406199 -0600 >+ # is_enabled - integer. Nit: Boolean actually. It may technically be an integer, but conceptually it is bool. >+ # is_default - integer (true if user has no preference) Likewise (and integers cannot even have the value "true" ;-)). >+<table> >+ <tr> >+ <th align="right">Setting Text:</th> >+ <td>A description of what the Setting controls</td> >+ </tr> >+ <tr> >+ <th align="right">Current Value:</th> >+ <td>A list of choices for this Setting; your current value is displayed. Only relevant if 'Use Default' is not checked.</td> >+ </tr> >+ <tr> >+ <th align="right">Default Value:</th> >+ <td>The default value of this Setting for this site</td> >+ </tr> >+ <tr> >+ <th align="right">Use Default:</th> >+ <td>If checked, the default value will always be used; if the Admin changes the default, your Setting will change too.</td> >+ </tr> >+ <tr> >+ <td colspan="2"><hr></td> >+ </tr> >+</table> I think all of this can be removed. Really, "A list of choices for this Setting; your current value is displayed" isn't particularly necessary for a person generally able to use Bugzilla anyway. :-) >+[% IF settings.size %] >+ <table border cellpadding="4"> >+ <tr> >+ <th>Setting Text</th> >+ <th>Current Value</th> >+ <th>Default Value</th> >+ <th>Use Default</th> >+ <tr> Nit: You're missing a "</tr>" here. You can also remove these column headings. Just having the setting descriptions and the combo boxes will do quite well if we have no other columns (option 2 above). >+ <label for="[% name %]"> >+ <select name="[% name %]" id="[% name %]"> >+ [% FOREACH x = settings.${name}.legal_values %] >+ <option value="[% x FILTER html %]" >+ [% " selected=\"selected\"" IF x == settings.${name}.value %]> >+ [% setting_descs.${x} OR x FILTER html %] >+ </option> >+ [% END %] >+ </select> >+ </label> The label element is unnecessary here; you can just remove it. >+ <td align=center> Quotes around center. Repeat a few lines further down. >+ [% setting_descs.${settings.${name}.default_value} FILTER none %] Why filter none? In general, why do we treat setting_descs as already HTML encoded? Also, should we have a fallback here in case setting_descs doesn't have the proper translation? >+ [% " checked=\"checked\"" IF settings.${name}.is_default %]> No need to change this if you don't want to, but a tip for the future: you can write " checked='checked'" or ' checked="checked"' to get rid of the ugly backslashes. >+++ tip.userprefs/template/en/default/admin/settings/edit.html.tmpl 2005-02-09 17:15:13.974798651 -0600 >+ # settings: array of hashes, keyed by setting_name. How is this an "array of hashes"? I'd expect this to be a "hash of hashes", huh? >+ # Each hash contains: >+ # is_enabled - integer. >+ # legal_values - array of strings >+ # default_value - string (global default for this setting) >+ # value - string (user-defined preference) >+ # is_default - integer (true if user has no preference) As above with the integer/boolean stuff. What is that value in this context anyway? >+ <br> >+ The Default Value displayed for each Setting will apply to all users who >+ do not choose their own value, and to anyone who is not logged in.<br> >+ <br> >+ The 'Enabled' checkbox controls whether or not this Setting is available to users.<br> >+ If it is checked, users will see this Setting on their User Preference page, >+ and will be allowed to choose their own value if they desire.<br> >+ If it is not checked, this Setting will not apppear on the User Preference page, >+ and the Default Value will automatically apply to everyone.<br> >+ <br> Use <p>...</p> elements to delimit paragraphs. Nit: "User preferences page" (note the plural: preferences). >+ <tr><td colspan=2><hr></td></tr> Quote "2" >+ <label for="[% name %]"> >+ <select name="[% name %]" id="[% name %]"> >+ [% FOREACH x = settings.${name}.legal_values %] >+ <option value="[% x FILTER html %]" >+ [% " selected=\"selected\"" IF x == settings.${name}.default_value %]> >+ [% setting_descs.${x} OR x FILTER html %] >+ </option> >+ [% END %] >+ </select> >+ </label> As above for the label. >+ <input type="checkbox" >+ name="[% checkbox_name %]" >+ id="[% checkbox_name %]" Technically, the name might not be a valid ID (not necessarily a valid name either, but more likely so). I'm not sure if I really care that much, but it would be rather easy to prefix it with a letter and make sure no illegal characters occur. This comment applies for numerous other lines as well. >+ <td> >+ <input type="submit" value="Submit Changes"> >+ </td> Nit: Closing td indented one space too much. >+ [% ELSE %] >+ There are no Settings to edit. >+ [% END %] On this template, Settings tend to be capitalized. I'd say "settings", despite all the architectural finesse involved. ;-) Footer links: ============= >+ [% ' | <a href="editparams.cgi">Parameters</a> ' _ >+ ' | <a href="editsettings.cgi">Settings</a>' We can't have "parameters" and "settings" side by side, they mean quite the same to the normal person (including me). "User settings" is fine with me, even if it is somewhat suboptimal. -- As I already told Travis on IRC, it's a good patch and the structure feels solid. All that despite my previous comments ;-) Some parts still need a bit of hammering and polish, but we're definitely getting there this time.
Attachment #173917 - Flags: review?(jouni) → review-
(In reply to comment #42) > ... if we can make life easier > for people using administrative tools for relatively little effort, > I don't see the downside. Downside is that: "SELECT foo FROM bar WHERE baz AND (condition2 OR condition3) OR condition3" is not as easy for a human to parse or maintain as: q{SELECT foo FROM bar WHERE baz AND (condition 1 OR condition2) OR condition3} Human readability and ease of maintenance is important too when we're dealing with this many programmers. Yes, it's possible to do something akin to the above with quotes and concatenation around every line instead of a single q{}, but now you're adding a LOT more characters, obfuscating readability again, and significantly compounding the chance of syntactical error (forgetting a quote or a period). > It doesn't return a hash reference. Okay, fine, my mistake... a *list* reference. :) I still don't see how that's useful when I want three distinct and different pieces of data, but maybe you can show me why this is more effective. I'm willing to learn, I'm just not convinced atm. (I did change other places based on your earlier pre-formal review suggestions, but this didn't seem like a useful place to try and shoehorn it in.) > Actually, it's fairly useful to have a sortindex that's a smallint. It helps > for creating values like 100, 200, 300, 400, in case you plan to possibly put a > lot of values between them in the future and you don't know how they're going to > sort. Fair enough. This is a good reason for it, and doing it this way doesn't hurt anything. I'll change it. > All the sortindexes in the enums patch are smallints, too. OTOH, this (IMHO) is *not* a good reason. "I did it this way, so you should too" is a nit, not an r-. I too believe in that consistency is good, but I balk at doing something ONLY for the sake of consistency when the 'inconsistent' way has merit too. (This stance also explains my reasons for my resistance to the one-line-SQL comments above. Right now, there are pros and cons to either side. If we adopt one or the other as canon, I'd go along with it because That's The Way It's Done. I don't see that as having happened, though, so it's still up to each programmer to decide which factors are more important to them, and code accordingly. I've asked Dave to take a look at the bug, though, and decide if there is going to be a Developers' Guide change for this recommendation.)
(In reply to comment #45) > Downside is that: > "SELECT foo FROM bar WHERE baz AND (condition2 OR condition3) OR condition3" > > is not as easy for a human to parse or maintain as: . Yeah. I was thinking about that, too, the other day. I think that we should definitely go one way or the other, and make that the standard as much as possible. I do tend to do: "SELECT foo, bar, baz" . " FROM qux" . "WHERE shazam = ?" > > It doesn't return a hash reference. > > Okay, fine, my mistake... a *list* reference. :) I still don't see how that's > useful when I want three distinct and different pieces of data, but maybe you > can show me why this is more effective. I'm willing to learn, I'm just not > convinced atm. OK! :-) my ($foo, $bar, $baz) = $dbh->selectrow_array($query, undef, $value); > > All the sortindexes in the enums patch are smallints, too. > > OTOH, this (IMHO) is *not* a good reason. I know. :-) I just threw it in there. :-)
(In reply to comment #40) > Nit: If that all returns only one row, it can be a selectrow_arrayref. (In reply to comment #42) > It doesn't return a hash reference. selectrow_arrayref. (In reply to comment #46) > my ($foo, $bar, $baz) = $dbh->selectrow_array($query, undef, $value); Oh, well, if you're going to change the name of the function, then yeah... I can see how this could be useful. Everywhere else (including the pre-formal review) you kept harping on how I should use selectrow_arrayref, though, so you can understand my confusion. :)
(In reply to comment #44) > The patch crashes when going to userprefs.cgi with no setting entries in the > DB. Either we ship with some, or we fix this so it won't crash. Latter would probably be good, but the former is definitely going to be the case. I have a patch on bug 41972 ready to go as soon as this lands, which will add the first Setting to the table. > >+# bless ($self, $class); > Nit: Why is this here, commented out? Because I saw it in a few other .pm files, but didn't know what it did; I had it here as a mental note to ask and learn, but then forgot to do either. > So what I'd like to see is options like > Use quips at the top of bug list? [ |v] > | Site default (No) | > | Yes | > | No | > | Randomly | > +--------------------+ I like this idea a lot. I'll work to implement it. > >+ [% setting_descs.${settings.${name}.default_value} FILTER none %] > Why filter none? In general, why do we treat setting_descs as already HTML > encoded? We don't. Three instances on that page, two are 'FILTER html'. This one was a mistake/oversight. > Also, should we have a fallback here in case setting_descs doesn't > have the proper translation? Yes. I added the fallback very late in the process, and simply missed this instance. Thanks. > >+++ tip.userprefs/template/en/default/admin/settings/edit.html.tmpl 2005-02- 09 17:15:13.974798651 -0600 > >+ # settings: array of hashes, keyed by setting_name. > How is this an "array of hashes"? I'd expect this to be a "hash of hashes", At the time that I wrote the interface docs, it *was* an array of hashes. It got changed later in the design process (to a hash of hashes) and I forgot to update the docs. Good catch. > We can't have "parameters" and "settings" side by side, they mean quite the > same to the normal person (including me). "User settings" is fine with me, even > if it is somewhat suboptimal. I'd like to leave it as Settings, although I'll change it if it's a sticking point. Ultimately, what I want is for 'Settings' to be another panel on a multi- panel Parameters page, but that can't happen until Dave (codes and) lands bug 46296. As usual, an excellent review. Thanks, Jouni!
(In reply to comment #47) > Oh, well, if you're going to change the name of the function, then yeah... I > can see how this could be useful. Everywhere else (including the pre-formal > review) you kept harping on how I should use selectrow_arrayref, though, so you > can understand my confusion. :) Yeah. :-) I only recently realized that selectrow has a pure "array" version, and doesn't need an arrayref. And that thing about hashref was just me being confused for a second. :-) I realized right after I posted that I meant "array," not "hash." :-)
(In reply to comment #45) > > Actually, it's fairly useful to have a sortindex that's a smallint. > > It helps for creating values like 100, 200, 300, 400, in case you plan > > to possibly put a lot of values between them in the future and you don't > > know how they're going to sort. I *knew* I had a problem with this logic, but it's taken me three days to remember what it was. I completely agree that this is a valid comment for *user-created* smallindices, like those for flags, which may be modified in the future by a User or Admin. This index, however, is only accessible by the programmer, and is not modifiable in any way by any user. The programmer who creates a setting (i.e. the person who fixes one of the bugs that this bug blocks) is the one who defines the sort order and choices, and the likelihood is that nobody else should ever be touching it beyond that point. For that reason, I think that it works perfectly well as a tinyint. It's no real skin off my nose regardless; I've already made the changes in the code, and I don't care enough to change it back, but it's been bugging me that I examined and rejcted this logic once already during the design process, then couldn't remember the reasoning when you brought it up.
Attached patch Code patch for tip, take 2 (obsolete) (deleted) — Splinter Review
This patch incorporates most of the suggestions above. Max: - I have not taken newlines out of SQL, as Dave has a good comment on that bug about attacking the problem in a single location instead of stamping out dozens of little fires. - I also did not take out 'use vars' as your suggested replacement looked like more code than the original. - renamed all units as suggested Jouni: - Tried to keep it below 80 chars everywhere. If anyone finds one and it doesn't look absolutely necessary, please mention it. - I *just* realized that I forgot the 'syntactic sugar' you mentioned. If there's a third version, I'll do it then; if not, I'll make a 3rd version with one in it. - now passes testing suite - renamed all units as suggested - GetLegalValues now called only when info is needed for editsettings.cgi or userprefs.cgi - Changed userprefs.cgi UI to work as you suggested; now consists only of string and picklist - Removed extraneous explanatory text from userprefs.cgi page ... and about everything else you asked for. :) I did not fix all nits, as I didn't agree with all of them. (For the most part, though I did.) Since they're just nits, however, I did not bring them up to argue about them. Thanks to both of you for reviews!
Attachment #173917 - Attachment is obsolete: true
Attachment #174615 - Flags: review?(jouni)
Attachment #174615 - Flags: review?(mkanat)
This is a very good idea. I would not keep this from landing to slide this in, but isn't a varchar(32) a bit small especially because we have bugs depending on this to store column-list orders, etc...??
Comment on attachment 174615 [details] [diff] [review] Code patch for tip, take 2 >--- tip/Bugzilla/User/Setting.pm 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/Bugzilla/User/Setting.pm 2005-02-17 14:58:50.682724772 -0600 >+ # Confirm that the $setting_name is properly formed; >+ # if not, throw a code error. Please add a comment saying "Note that the due to the way this is currently used, setting names must conform to the limitations set for HTML NAMEs and IDs." (or something equivalent) >+ # If there's nothing left after taking out the setting and ID Nit: "If there were only two parameters" rather than this explanation; the exact method of popping the param stack isn't quite as descriptive as the parameter count -based explanation. >+ $dbh->selectrow_array( >+ q{SELECT default_value, is_enabled, setting_value >+ FROM setting >+ LEFT JOIN profile_setting >+ ON setting.name = profile_setting.setting_name >+ WHERE name = ? >+ AND (profile_setting.user_id = ? >+ OR profile_setting.user_id IS NULL)}, >+ undef, $setting_name, $user_id); Nit: Why is the last line indented like that? I'd expect the u of the undef to be aligned with the q that starts the first parameter. >+ # Don't flog the db with requests for legal values >+ # unless they're needed. >+ my $legal_values = 0; >+ $get_legal_values && do {$legal_values = _get_legal_values($name);}; Re this, I think this is an acceptable approach, but it would be a better idea to refactor this code a bit to avoid repeating the logic and also to keep the code API better documented (after all, you shouldn't have know _at object construction time_ whether the legal values should are going to be needed). Make "get_legal_values" a method of the Setting object, and retrieve the data in it (caching internally as necessary for multiple invocation of the method). This way the object construction would never load that data, and it would always be loaded automatically when needed. How does that sound? (well, we discussed this a bit on IRC - but I'll still post the suggestion here) >--- tip/template/en/default/global/setting-descs.none.tmpl 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/template/en/default/global/setting-descs.none.tmpl 2005-02-10 14:32:09.000000000 -0600 >--- tip/template/en/default/global/setting-descs.none.tmpl.for_testing 1969-12-31 18:00:00.000000000 -0600 >+++ tip.userprefs/template/en/default/global/setting-descs.none.tmpl.for_testing 2005-02-10 14:32:21.000000000 -0600 FWIW, these two are equal. This won't matter for checkin of course, but the other reviewers might want to pull the descs from the 2nd attachment of this bug. Re this: >> We can't have "parameters" and "settings" side by side, they mean quite the >> same to the normal person (including me). "User settings" is fine with me, >> even if it is somewhat suboptimal. >I'd like to leave it as Settings, although I'll change it if it's a sticking >point. Ultimately, what I want is for 'Settings' to be another panel on a multi- >panel Parameters page, but that can't happen until Dave (codes and) lands bug >46296. Ultimately, we all want that sandwich-making machine ;-) Since we're going to have to update the administration documents and UI twice anyway (once for adding this feature, the second time integrating it into the multi-panel params), it's simply a question of how to name it. Settings and Parameters mean essentially the same (to the normal person), thus they cannot exist side-by-side meaning two fairly different aspects of Bugzilla configuration. I still want it changed, and vote for "User settings" in absence of a better term. My thanks on a job well done - with patches of this size, it's fairly common to have to comment on the same things twice. You did exceptionally well in getting all the comments integrated and/or responded to. That really makes reviewing much smoother. Although I'd like to see the API issue with loading the legal values being addressed, the current solution is acceptable as well. I'm confident in your ability to pick a solution and implement it properly. :-) Knowing the schedule I will have in the near future (lots of vacations and other interruptions), I don't want to be the one holding this up. Therefore, r=jouni with the fixes mentioned above.
Attachment #174615 - Flags: review?(jouni) → review+
Attached patch Code patch for tip, take 3 (obsolete) (deleted) — Splinter Review
- Fixed the legal_values issue as per IRC discussions with Jouni. - Fixed all nits/comments made by Jouni in comment #53 - Added syntactic sugar subroutine setting_value() Approval for r+ in advance by jouni considering those, but asking for one final once-over from Max too, just to keep it all legal. :)
Attachment #174615 - Attachment is obsolete: true
Attachment #175228 - Flags: review?(mkanat)
Attachment #174615 - Flags: review?(mkanat)
Comment on attachment 175228 [details] [diff] [review] Code patch for tip, take 3 >+ if ( !($setting_name =~ /^[a-zA-Z][-.:\w]*$/) ) { >+ ThrowCodeError("setting_name_invalid", { name => $setting_name }); >+ } Maybe we should have a validate_setting_name function some day... >+ undef, >+ $setting_name, $user_id); Nit: Those arguments can all be on the same line. Also, I think the General Settings tab should be next to Account Settings. Only one actual review- point: Now that get_legal_values is public, it needs POD docs.
Attachment #175228 - Flags: review?(mkanat) → review-
(In reply to comment #55) > Maybe we should have a validate_setting_name function some day... That's sort of what this is. Remember that only developers will be adding settings, not users or administrators. Given that, even a single run of testing will cause this code to spit an error. > Only one actual review- point: Now that get_legal_values is public, it needs > POD docs. This confuses me. Are you saying that only get_legal_values needs POD documentation? Or, having done those, would you then r- me for not having POD docs for everything else in the unit? (Or did you miss the fact that there are no POD documents for Setting.pm?)
Oh, I must have missed that there are no POD docs for Setting.pm. It does need POD docs. Would you write them for another bug if we check this in now?
(In reply to comment #57) > It does need POD docs. Would you write them for another bug > if we check this in now? Absolutely... and much preferred. I don't want to have to do three more iterations of this while I get those right, if at all possible. OTOH, if this gets checked in, people can start working on the dependents while I finish up the internal documentation. Created bug 283357 to track that.
Blocks: 283357
Comment on attachment 175228 [details] [diff] [review] Code patch for tip, take 3 OK, r=mkanat, then. :-)
Attachment #175228 - Flags: review- → review+
Flags: approval?
Flags: approval? → approval+
Whiteboard: patch awaiting review → DO NOT CHECK IN until bug 41972 is ready to land
Attached patch updated to avoid bitrot (obsolete) (deleted) — Splinter Review
Patch has rotted a bit in the last couple of weeks (not much, though), and I found two small errors while implementing bug 41972. Fixed both of those. I also removed the 'syntactic sugar' I added in v3, as it turned out not to be worth it. Carrying r+ forward.
Attachment #175228 - Attachment is obsolete: true
Attachment #176184 - Flags: review+
Blocks: 182238
Attached patch Code patch for tip, take 5 (obsolete) (deleted) — Splinter Review
I know... once you've got r+ you're supposed to stop changing things. :) In fixing bug 41972, however, I added some code there that really belongs in this bug -- namely, the changes to checksetup.pl and code-error.html.tmpl. I justified it by saying to myself that they were going in at the same time, but since I'm making a new patch *anyway*, I may as well include them in the proper place. The new patch is needed because I've moved the Bugzilla::User::delete_setting to Bugzilla::User::Setting::reset_to_default, and Bugzilla::User::set_setting Bugzilla::User::Setting::set. The make more sense as methods within Setting.pm than within User. I also made legal_setting into a proper method, so very nicely fixes up the problem Jouni raised earlier. Finally, I added POD documentation to Setting.pm, as was asked for in bug 283357; I figured that I was respinning the patch anyway, so I may as well do it properly from the ground up. Max, some of your comments (about placeholders) from bug 41972 now transfer over here, since the code moved; I fixed them as part of this patch.
Attachment #176184 - Attachment is obsolete: true
Attachment #176779 - Flags: review?(mkanat)
Comment on attachment 176779 [details] [diff] [review] Code patch for tip, take 5 >+sub get_legal_values { >+ my ($self) = @_; >+ my $dbh = Bugzilla->dbh; This probably ought to have a $self->{legal_values} hash to cache itself in. >+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'}); You also need to actually change the in-memory Setting, too. That is, the $self->{is_default} value and so forth. >+sub set { [snip] >+ $dbh->do($query, undef, $value, $self->{'_setting_name'}, $self->{'_user_id'}); Same for this one, too. You need to change the $self->{value} value. >+=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. You'll want to note that these are usually accessed through the "settings" object of a user, and not directly. >+=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. We usually indent the stuff all the way out to where the text for Description starts, for all of the parts. So, for example: 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. >+ >+# tables for the 'settings' >+ Just to let you know, these are going to have to go into the Bugzilla::DB::Schema structure. You'll probably want to start looking at the final patch on bug 146679 for an idea of how that works. I'll send out an email to developers@ after I check that code in, explaining it. >+########################################################################### >+# Populate setting tables >+########################################################################### I'd really like it if these subs could go into Bugzilla::User::Setting, and then we did a require Bugzilla::User::Setting; import Bugzilla::User::Setting qw(setting_exists add_setting); (You can't call 'use' in checksetup.) >+ # We need to obtain the list of legal values for each >+ # setting, so it can be displayed on the UI picklist >+ foreach my $name (@setting_list) { >+ $vars->{'settings'}->{$name}->{'legal_values'} = >+ $vars->{'settings'}->{$name}->get_legal_values; >+ } You know, now that it's a method of the Setting objects, you don't even really need to do that. You can just pass the Settings array into the template and get the legal_values object out of each one. Same deal with userprefs.cgi.
Attachment #176779 - Flags: review?(mkanat) → review-
Attached patch Code patch for tip, take 6 (obsolete) (deleted) — Splinter Review
Alright... I have updated this to use the new DB::SCHEMA, moved the subroutines from checksetup into Setting.pm, and addressed pretty much everything else you mentioned. Should be good to go.
Attachment #176779 - Attachment is obsolete: true
Attachment #176887 - Flags: review?(mkanat)
Comment on attachment 176887 [details] [diff] [review] Code patch for tip, take 6 >+ # SETTINGS >+ # -------- [snip] Yay, this all looks perfect! And thank you for the descriptive comments, too! :-D >+sub add_setting { >+ my ($name, $values, $default_value) = @_; >+ my $dbh = Bugzilla->dbh; >+ >+ return if setting_exists($name); Forgot the underscore. >+sub _setting_exists { >+ my ($setting_name) = @_; >+ my $dbh = Bugzilla->dbh; >+ my $sth = $dbh->prepare("SELECT name FROM setting WHERE name = ?"); >+ $sth->execute($setting_name); >+ return ($sth->rows) ? 1 : 0; >+} Nit: A perhaps nicer way to do this would be: return $dbh->selectrow_array("SELECT COUNT(*) FROM setting WHERE name = ?", undef, $setting_name); >+sub get_legal_values { Nit: At this point, it's probably better just to call it legal_values. But it's not a huge deal. >+=item C<add_setting($user_id)> Missing the other two vars in the prototype, here. >--- tip/Bugzilla/User.pm 2005-03-07 12:10:57.000000000 -0600 >+++ tip.quips/Bugzilla/User.pm 2005-03-09 01:00:30.000000000 -0600 [snip] > > use base qw(Exporter); Did we remove the setting_value export from this patch? Everything else looks great. :-)
Attachment #176887 - Flags: review?(mkanat) → review-
Attached patch Code patch for tip, take 7 (obsolete) (deleted) — Splinter Review
> Forgot the underscore. Fixed > Nit: A perhaps nicer way to do this would be: At this point, I don't really care. :) Untouched. > Nit: At this point, it's probably better just to call it legal_values. This I agree with, care about, and fixed. > Missing the other two vars in the prototype, here. Fixed > Did we remove the setting_value export from this patch? Yes, the Export was removed from this patch. The actual subroutine was removed a couple of patches back, but I forgot about the Export, and just caught it this time around.
Attachment #176887 - Attachment is obsolete: true
Attachment #176893 - Flags: review?(mkanat)
Comment on attachment 176893 [details] [diff] [review] Code patch for tip, take 7 Great. Interdiff looks right. :-)
Attachment #176893 - Flags: review?(mkanat) → review+
Comment on attachment 176893 [details] [diff] [review] Code patch for tip, take 7 NOTE: 'default_value' should just be 'value' on both of the following lines: + default_value => {TYPE => 'varchar(32)', NOTNULL => 1}, + setting_value_nv_unique_idx => {FIELDS => [qw(name default_value)], I will be fixing this on checkin.
Attached patch Code patch for tip, take 8 (obsolete) (deleted) — Splinter Review
This fixes a typo in the Schema that travis mentioned in IRC.
Attachment #176893 - Attachment is obsolete: true
Attachment #176904 - Flags: review+
Attached patch Code patch for tip, take 8.1 (deleted) — Splinter Review
Oh, fixed it in the index, too.
Attachment #176904 - Attachment is obsolete: true
Attachment #176906 - Flags: review+
/cvsroot/mozilla/webtools/bugzilla/checksetup.pl,v <-- checksetup.pl new revision: 1.365; previous revision: 1.364 done RCS file: /cvsroot/mozilla/webtools/bugzilla/editsettings.cgi,v done Checking in editsettings.cgi; /cvsroot/mozilla/webtools/bugzilla/editsettings.cgi,v <-- editsettings.cgi initial revision: 1.1 done Checking in userprefs.cgi; /cvsroot/mozilla/webtools/bugzilla/userprefs.cgi,v <-- userprefs.cgi new revision: 1.70; previous revision: 1.69 done Checking in Bugzilla/User.pm; /cvsroot/mozilla/webtools/bugzilla/Bugzilla/User.pm,v <-- User.pm new revision: 1.45; previous revision: 1.44 done Checking in Bugzilla/DB/Schema.pm; /cvsroot/mozilla/webtools/bugzilla/Bugzilla/DB/Schema.pm,v <-- Schema.pm new revision: 1.5; previous revision: 1.4 done RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/User/Setting.pm,v done Checking in Bugzilla/User/Setting.pm; /cvsroot/mozilla/webtools/bugzilla/Bugzilla/User/Setting.pm,v <-- Setting.pm initial revision: 1.1 done Checking in template/en/default/filterexceptions.pl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/filterexceptions.pl,v <- - filterexceptions.pl new revision: 1.37; previous revision: 1.36 done Checking in template/en/default/account/prefs/prefs.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/account/prefs/prefs.html. tmpl,v <-- prefs.html.tmpl new revision: 1.17; previous revision: 1.16 done RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/account/prefs/setti ngs.html.tmpl,v done Checking in template/en/default/account/prefs/settings.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/account/prefs/settings.ht ml.tmpl,v <-- settings.html.tmpl initial revision: 1.1 done RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/admin/settings/edit .html.tmpl,v done Checking in template/en/default/admin/settings/edit.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/admin/settings/edit.html. tmpl,v <-- edit.html.tmpl initial revision: 1.1 done RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/admin/settings/upda ted.html.tmpl,v done Checking in template/en/default/admin/settings/updated.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/admin/settings/updated.ht ml.tmpl,v <-- updated.html.tmpl initial revision: 1.1 done Checking in template/en/default/global/code-error.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/code- error.html.tmpl,v <-- code-error.html.tmpl new revision: 1.47; previous revision: 1.46 done Checking in template/en/default/global/field-descs.none.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/field- descs.none.tmpl,v <-- field-descs.none.tmpl new revision: 1.7; previous revision: 1.6 done RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/setting- descs.none.tmpl,v done Checking in template/en/default/global/setting-descs.none.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/setting- descs.none.tmpl,v <-- setting-descs.none.tmpl initial revision: 1.1 done Checking in template/en/default/global/useful-links.html.tmpl; /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/useful- links.html.tmpl,v <-- useful-links.html.tmpl new revision: 1.35; previous revision: 1.34 done
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Flags: documentation?
Keywords: relnote
Resolution: --- → FIXED
Summary: Create a general user preferences table → Create a user preferences infrastructure (became 'General Settings')
Whiteboard: DO NOT CHECK IN until bug 41972 is ready to land
Went to see what this looks like, and http://landfill.bugzilla.org/bugzilla-tip/userprefs.cgi?tab=settings says: "Can't use an undefined value as a HASH reference at /var/www/html/bugzilla- tip/userprefs.cgi line 146." Problem with this patch or with landfill?
Neither; there's just no Settings defined yet. That's why I had to wait for bug 41972 to be ready before this could land... but I got called away after committing one before I could commit the other. Give it another 20 minutes, then check out the tip again. :)
No longer blocks: 283357
*** Bug 283357 has been marked as a duplicate of this bug. ***
Blocks: 116863
Added to the release notes in bug 286274.
Keywords: relnote
Blocks: 182083
Flags: documentation? → documentation+
No longer blocks: 26257
QA Contact: matty_is_a_geek → default-qa
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: