Merge lp:~adeuring/launchpad/bug-903852 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 14525
Proposed branch: lp:~adeuring/launchpad/bug-903852
Merge into: lp:launchpad
Diff against target: 570 lines (+247/-148)
7 files modified
lib/lp/bugs/browser/bugtask.py (+29/-1)
lib/lp/bugs/browser/tests/test_bugtask.py (+16/-0)
lib/lp/bugs/interfaces/bugtask.py (+3/-7)
lib/lp/bugs/javascript/buglisting_utils.js (+5/-1)
lib/lp/bugs/javascript/tests/test_buglisting_utils.js (+70/-30)
lib/lp/bugs/model/bugtask.py (+93/-92)
lib/lp/bugs/templates/bugtask-macros-tableview.pt (+31/-17)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-903852
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+85651@code.launchpad.net

Commit message

[r=abentley][bug=903852] add JS sort buttons for all sort orders implemented by BugTaskSet.search()

Description of the change

The new JS widgets to change the sort order and to select the displyed data
break at present when a bug listing page is accessed with an "unexpected"
sort order. This can happen for example when a user selects "sort by
numbers of users affected" in the "Advanced search" page. An example URL
for the bug:

https://bugs.launchpad.net/launchpad/+bugs?orderby=-users_affected_count

(make sure that the feature bugs.dynamic_bug_listings.enabled is enabled.)

The cause is simple: The JS code in
lp/bugs/templates/bugtask-macros-tableview.pt, macro activate_listing_js,
does not define sort buttons for all sort orders implemented by
BugTaskSet.search()

A related problem: Generally, we want to show only sort buttons for data
that is also displayed on the page. A strict implementation of this rule
would be confusing for users: If the data for current sort order is not
displayed, users would have no indication what the sort order is (well,
the< could have a look at the URL, but that's not obvious to all users,
and it can be cumbersome to find the "orderby" parameter in complex
queries).

Hence there is already an exemption to this rule: The button for the
current sort order is never hidden. But the button disappears when
the users selects a different sort order.

This branch adds another exemption: If the data for the sort order
selected during page load can never be displayed, the sort button will
always be displayed during the "Lifetime" of the page view.

Implementation details:

I moved the definition of the set of all sort buttons (parameter
sort_keys for Y.lp.ordering.OrderByBar()) to
lp.browser.bugtask; this data is added in an initialize() call to the
JSON cache.

This makes it possible to write a test that sort_keys (or more precisely,
the new constant lp.browser.bugtask.SORT_KEYS) has entries for all
sort options implemented by BugTaskSet.search()
(test_sort_keys_in_json_cache()).

This required another, mostly mechanical, change: The SQL expressions
for the ORDER BY clause were stored in BugTaskSet._ORDERBY_COLUMN,
which is not initialized during module load to avoid circular imports.
Instead, this dictionary was initilalized in the method
BugTaskSet.getOrderByColumn(). I replaced this method with the
cached property BugTaskSet.orderby_expression, so that
test_sort_keys_in_json_cache() can access all keys of this dictionary.

tests:

./bin/test bugs -vvt test_bugtask.*test_sort_keys_in_json_cache
./bin/test bugs -vvt test_buglisting_utils

lint:

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/browser/bugtask.py
  lib/lp/bugs/browser/tests/test_bugtask.py
  lib/lp/bugs/interfaces/bugtask.py
  lib/lp/bugs/javascript/buglisting_utils.js
  lib/lp/bugs/javascript/tests/test_buglisting_utils.js
  lib/lp/bugs/model/bugtask.py
  lib/lp/bugs/templates/bugtask-macros-tableview.pt

./lib/lp/bugs/templates/bugtask-macros-tableview.pt
     674: not well-formed (invalid token)
make: *** [lint] Fehler 1

This is caused by the '<' in

      for (var i = 0; i < sort_keys.length; i++)

This complaint could be avoided by

      for (var i = 0; sort_keys.length > i; i++)

but at least for me the former variant is easier for "mental parsing".

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

Nice improvement.

A couple of small tweaks:
- Our style guide recommends "!Y.Lang.isUndefined()" (or isValue) rather than "!== undefined".
- You should be able to avoid the lint warning, yet write a readable loop using Y.each.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugtask.py'
2--- lib/lp/bugs/browser/bugtask.py 2011-12-14 03:19:49 +0000
3+++ lib/lp/bugs/browser/bugtask.py 2011-12-15 10:18:27 +0000
4@@ -2478,6 +2478,33 @@
5 return Link('+nominations', 'Review nominations', icon='bug')
6
7
8+# All sort orders supported by BugTaskSet.search() and a title for
9+# them.
10+SORT_KEYS = [
11+ ('id', 'Bug number'),
12+ ('title', 'Bug title'),
13+ ('importance', 'Importance'),
14+ ('status', 'Status'),
15+ ('heat', 'Bug heat'),
16+ ('reporter', 'Reporter'),
17+ ('assignee', 'Assignee'),
18+ ('targetname', 'Package/Project/Series name'),
19+ ('milestone_name', 'Milestone'),
20+ ('datecreated', 'Bug age'),
21+ ('date_last_updated', 'Date bug last updated'),
22+ ('tag', 'Bug Tags'),
23+ ('date_closed', 'Date bug closed'),
24+ ('dateassigned', 'Date when the bug task was assigned'),
25+ ('number_of_duplicates', 'Number of duplicates'),
26+ ('latest_patch_uploaded', 'Date latest patch uploaded'),
27+ ('message_count', 'Number of comments'),
28+ ('milestone', 'Milestone ID'),
29+ ('specification', 'Linked blueprint'),
30+ ('task', 'Bug task ID'),
31+ ('users_affected_count', 'Number of affected users'),
32+ ]
33+
34+
35 class BugTaskSearchListingView(LaunchpadFormView, FeedsMixin, BugsInfoMixin):
36 """View that renders a list of bugs for a given set of search criteria."""
37
38@@ -2672,6 +2699,7 @@
39 last_batch = batch_navigator.batch.lastBatch()
40 cache.objects['last_start'] = last_batch.startNumber() - 1
41 cache.objects.update(_getBatchInfo(batch_navigator.batch))
42+ cache.objects['sort_keys'] = SORT_KEYS
43
44 @property
45 def show_config_portlet(self):
46@@ -2743,7 +2771,7 @@
47 orderby_col = orderby_col[1:]
48
49 try:
50- bugset.getOrderByColumnDBName(orderby_col)
51+ bugset.orderby_expression[orderby_col]
52 except KeyError:
53 raise UnexpectedFormData(
54 "Unknown sort column '%s'" % orderby_col)
55
56=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
57--- lib/lp/bugs/browser/tests/test_bugtask.py 2011-12-13 15:32:36 +0000
58+++ lib/lp/bugs/browser/tests/test_bugtask.py 2011-12-15 10:18:27 +0000
59@@ -2246,6 +2246,22 @@
60 mustache_model['bugtasks'][0]['show_date_last_updated'] = True
61 self.assertIn('Last updated updated1', navigator.mustache)
62
63+ def test_sort_keys_in_json_cache(self):
64+ # The JSON cache of a search listing view provides a sequence
65+ # that describes all sort orders implemented by
66+ # BugTaskSet.search() and no sort orders that are not implemented.
67+ with dynamic_listings():
68+ view = self.makeView()
69+ cache = IJSONRequestCache(view.request)
70+ json_sort_keys = cache.objects['sort_keys']
71+ json_sort_keys = set(key[0] for key in json_sort_keys)
72+ valid_keys = set(getUtility(IBugTaskSet).orderby_expression.keys())
73+ self.assertEqual(
74+ valid_keys, json_sort_keys,
75+ "Existing sort order values not available in JSON cache: %r; "
76+ "keys present in JSON cache but not defined: %r"
77+ % (valid_keys - json_sort_keys, json_sort_keys - valid_keys))
78+
79
80 class TestBugTaskExpirableListingView(BrowserTestCase):
81 """Test BugTaskExpirableListingView."""
82
83=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
84--- lib/lp/bugs/interfaces/bugtask.py 2011-12-12 04:16:26 +0000
85+++ lib/lp/bugs/interfaces/bugtask.py 2011-12-15 10:18:27 +0000
86@@ -920,7 +920,7 @@
87 """
88
89 def userHasBugSupervisorPrivileges(user):
90- """Is the user a privledged one, allowed to changed details on a
91+ """Is the user a privledged one, allowed to changed details on a
92 bug?
93
94 :return: A boolean.
95@@ -1483,6 +1483,8 @@
96 class IBugTaskSet(Interface):
97 """A utility to retrieving BugTasks."""
98 title = Attribute('Title')
99+ orderby_expression = Attribute(
100+ "The SQL expression for a sort key")
101
102 def get(task_id):
103 """Retrieve a BugTask with the given id.
104@@ -1639,12 +1641,6 @@
105 <user> is None, no private bugtasks will be returned.
106 """
107
108- def getOrderByColumnDBName(col_name):
109- """Get the database name for col_name.
110-
111- If the col_name is unrecognized, a KeyError is raised.
112- """
113-
114 def getBugCountsForPackages(user, packages):
115 """Return open bug counts for the list of packages.
116
117
118=== modified file 'lib/lp/bugs/javascript/buglisting_utils.js'
119--- lib/lp/bugs/javascript/buglisting_utils.js 2011-12-09 14:20:52 +0000
120+++ lib/lp/bugs/javascript/buglisting_utils.js 2011-12-15 10:18:27 +0000
121@@ -338,8 +338,12 @@
122 orderby_visibility[order_key] = data_visibility[data_key];
123 }
124 }
125- // Never hide the button for the current sort order.
126+ // Never hide the button for the current sort order...
127 orderby_visibility[orderbybar.get('active')] = true;
128+ // ...and for the sort order that should always be displayed.
129+ if (!Y.Lang.isUndefined(orderbybar.always_display)) {
130+ orderby_visibility[orderbybar.always_display] = true;
131+ }
132 orderbybar.updateVisibility(orderby_visibility);
133 }
134
135
136=== modified file 'lib/lp/bugs/javascript/tests/test_buglisting_utils.js'
137--- lib/lp/bugs/javascript/tests/test_buglisting_utils.js 2011-12-11 15:48:11 +0000
138+++ lib/lp/bugs/javascript/tests/test_buglisting_utils.js 2011-12-15 10:18:27 +0000
139@@ -436,43 +436,58 @@
140 Assert.isNull(Y.Cookie.get(this.cookie_name));
141 },
142
143+ getDefaultDataVisibility: function() {
144+ return {
145+ show_title: true,
146+ show_id: false,
147+ show_importance: true,
148+ show_status: true,
149+ show_heat: true,
150+ show_targetname: true,
151+ show_datecreated: false,
152+ show_date_last_updated: false,
153+ show_assignee: false,
154+ show_reporter: false,
155+ show_milestone_name: false,
156+ show_tag: true
157+ };
158+ },
159+
160+ SORT_KEYS: [
161+ ['id', 'Bug number'],
162+ ['title', 'Bug title'],
163+ ['importance', 'Importance'],
164+ ['status', 'Status'],
165+ ['heat', 'Bug heat'],
166+ ['reporter', 'Reporter'],
167+ ['assignee', 'Assignee'],
168+ ['targetname', 'Package/Project/Series name'],
169+ ['milestone_name', 'Milestone'],
170+ ['datecreated', 'Bug age'],
171+ ['date_last_updated', 'Date bug last updated'],
172+ ['tag', 'Bug Tags'],
173+ ['date_closed', 'Date bug closed'],
174+ ['dateassigned', 'Date when the bug task was assigned'],
175+ ['number_of_duplicates', 'Number of duplicates'],
176+ ['latest_patch_uploaded', 'Date latest patch uploaded'],
177+ ['message_count', 'Number of comments'],
178+ ['milestone', 'Milestone ID'],
179+ ['specification', 'Linked blueprint'],
180+ ['task', 'Bug task ID'],
181+ ['users_affected_count', 'Number of affected users']
182+ ],
183+
184 test_update_sort_button_visibility: function() {
185 // update_sort_button_visibility() hides sort buttons for
186 // data that is not displayed and shown sort buttons for other
187 // data.
188 var orderby = new Y.lp.ordering.OrderByBar({
189- sort_keys: [
190- ['id', 'Bug number'],
191- ['title', 'Bug title'],
192- ['importance', 'Importance'],
193- ['status', 'Status'],
194- ['heat', 'Bug heat'],
195- ['reporter', 'Reporter'],
196- ['assignee', 'Assignee'],
197- ['targetname', 'Package/Project/Series name'],
198- ['milestone_name', 'Milestone'],
199- ['datecreated', 'Bug age'],
200- ['date_last_updated', 'Date bug last updated'],
201- ['tag', 'Bug Tags']
202- ],
203+ sort_keys: this.SORT_KEYS,
204 active: 'importance',
205 sort_order: 'desc'
206 });
207 orderby.render();
208- var data_visibility = {
209- show_title: true,
210- show_id: false,
211- show_importance: true,
212- show_status: true,
213- show_heat: true,
214- show_targetname: true,
215- show_datecreated: false,
216- show_date_last_updated: false,
217- show_assignee: false,
218- show_reporter: false,
219- show_milestone_name: false,
220- show_tag: true
221- };
222+ var data_visibility = this.getDefaultDataVisibility();
223 Y.lp.buglisting_utils.update_sort_button_visibility(
224 orderby, data_visibility);
225 Y.each(orderby.get('li_nodes'), function(li_node) {
226@@ -483,16 +498,41 @@
227 Assert.isTrue(li_node._isHidden());
228 }
229 });
230+ },
231
232+ test_update_sort_button_visibility_current_sort_order: function() {
233 // The current sort order (importance for this test) is
234 // never hidden, even if the bug importance is not displayed.
235+ var orderby = new Y.lp.ordering.OrderByBar({
236+ sort_keys: this.SORT_KEYS,
237+ active: 'importance',
238+ sort_order: 'desc'
239+ });
240+ orderby.render();
241+ var data_visibility = this.getDefaultDataVisibility();
242 data_visibility.show_importance = false;
243 Y.lp.buglisting_utils.update_sort_button_visibility(
244 orderby, data_visibility);
245- importance_button = Y.one('#sort-importance');
246+ var importance_button = Y.one('#sort-importance');
247 Assert.isFalse(importance_button._isHidden());
248+ },
249+
250+ test_update_sort_button_visibility_extra_sort_order: function() {
251+ // If the orderby widget has a property "always_display",
252+ // the button for this sort order is never hidden.
253+ var orderby = new Y.lp.ordering.OrderByBar({
254+ sort_keys: this.SORT_KEYS,
255+ active: 'importance',
256+ sort_order: 'desc'
257+ });
258+ orderby.render();
259+ orderby.always_display = 'users_affected_count';
260+ var data_visibility = this.getDefaultDataVisibility();
261+ Y.lp.buglisting_utils.update_sort_button_visibility(
262+ orderby, data_visibility);
263+ var users_affected_button = Y.one('#sort-users_affected_count');
264+ Assert.isFalse(users_affected_button._isHidden());
265 }
266-
267 });
268
269
270
271=== modified file 'lib/lp/bugs/model/bugtask.py'
272--- lib/lp/bugs/model/bugtask.py 2011-12-12 04:16:26 +0000
273+++ lib/lp/bugs/model/bugtask.py 2011-12-15 10:18:27 +0000
274@@ -162,7 +162,10 @@
275 from lp.registry.model.pillar import pillar_sort_key
276 from lp.registry.model.sourcepackagename import SourcePackageName
277 from lp.services import features
278-from lp.services.propertycache import get_property_cache
279+from lp.services.propertycache import (
280+ cachedproperty,
281+ get_property_cache,
282+ )
283 from lp.soyuz.enums import PackagePublishingStatus
284 from lp.blueprints.model.specification import Specification
285
286@@ -3206,95 +3209,93 @@
287 # which will be converted into key-value pairs in the dictionary.
288 return dict(result)
289
290- def getOrderByColumnDBName(self, col_name):
291- """See `IBugTaskSet`."""
292- if BugTaskSet._ORDERBY_COLUMN is None:
293- # Avoid circular imports.
294- from lp.bugs.model.bug import (
295- Bug,
296- BugTag,
297- )
298- from lp.registry.model.milestone import Milestone
299- from lp.registry.model.person import Person
300- Assignee = ClassAlias(Person)
301- Reporter = ClassAlias(Person)
302- BugTaskSet._ORDERBY_COLUMN = {
303- "task": (BugTask.id, []),
304- "id": (BugTask.bugID, []),
305- "importance": (BugTask.importance, []),
306- # TODO: sort by their name?
307- "assignee": (
308- Assignee.name,
309- [
310- (Assignee,
311- LeftJoin(Assignee, BugTask.assignee == Assignee.id))
312- ]),
313- "targetname": (BugTask.targetnamecache, []),
314- "status": (BugTask._status, []),
315- "title": (Bug.title, []),
316- "milestone": (BugTask.milestoneID, []),
317- "dateassigned": (BugTask.date_assigned, []),
318- "datecreated": (BugTask.datecreated, []),
319- "date_last_updated": (Bug.date_last_updated, []),
320- "date_closed": (BugTask.date_closed, []),
321- "number_of_duplicates": (Bug.number_of_duplicates, []),
322- "message_count": (Bug.message_count, []),
323- "users_affected_count": (Bug.users_affected_count, []),
324- "heat": (BugTask.heat, []),
325- "latest_patch_uploaded": (Bug.latest_patch_uploaded, []),
326- "milestone_name": (
327- Milestone.name,
328- [
329- (Milestone,
330- LeftJoin(Milestone,
331- BugTask.milestone == Milestone.id))
332- ]),
333- "reporter": (
334- Reporter.name,
335- [
336- (Bug, Join(Bug, BugTask.bug == Bug.id)),
337- (Reporter, Join(Reporter, Bug.owner == Reporter.id))
338- ]),
339- "tag": (
340- BugTag.tag,
341- [
342- (Bug, Join(Bug, BugTask.bug == Bug.id)),
343- (BugTag,
344- LeftJoin(
345- BugTag,
346- BugTag.bug == Bug.id and
347- # We want at most one tag per bug. Select the
348- # tag that comes first in alphabetic order.
349- BugTag.id == SQL("""
350- SELECT id FROM BugTag AS bt
351- WHERE bt.bug=bug.id ORDER BY bt.name LIMIT 1
352- """))),
353- ]
354- ),
355- "specification": (
356- Specification.name,
357- [
358- (Bug, Join(Bug, BugTask.bug == Bug.id)),
359- (Specification,
360- LeftJoin(
361- Specification,
362- # We want at most one specification per bug.
363- # Select the specification that comes first
364- # in alphabetic order.
365- Specification.id == SQL("""
366- SELECT Specification.id
367- FROM SpecificationBug
368- JOIN Specification
369- ON SpecificationBug.specification=
370- Specification.id
371- WHERE SpecificationBug.bug=Bug.id
372- ORDER BY Specification.name
373- LIMIT 1
374- """))),
375- ]
376- ),
377- }
378- return BugTaskSet._ORDERBY_COLUMN[col_name]
379+ @cachedproperty
380+ def orderby_expression(self):
381+ # Avoid circular imports.
382+ from lp.bugs.model.bug import (
383+ Bug,
384+ BugTag,
385+ )
386+ from lp.registry.model.milestone import Milestone
387+ from lp.registry.model.person import Person
388+ Assignee = ClassAlias(Person)
389+ Reporter = ClassAlias(Person)
390+ return {
391+ "task": (BugTask.id, []),
392+ "id": (BugTask.bugID, []),
393+ "importance": (BugTask.importance, []),
394+ # TODO: sort by their name?
395+ "assignee": (
396+ Assignee.name,
397+ [
398+ (Assignee,
399+ LeftJoin(Assignee, BugTask.assignee == Assignee.id))
400+ ]),
401+ "targetname": (BugTask.targetnamecache, []),
402+ "status": (BugTask._status, []),
403+ "title": (Bug.title, []),
404+ "milestone": (BugTask.milestoneID, []),
405+ "dateassigned": (BugTask.date_assigned, []),
406+ "datecreated": (BugTask.datecreated, []),
407+ "date_last_updated": (Bug.date_last_updated, []),
408+ "date_closed": (BugTask.date_closed, []),
409+ "number_of_duplicates": (Bug.number_of_duplicates, []),
410+ "message_count": (Bug.message_count, []),
411+ "users_affected_count": (Bug.users_affected_count, []),
412+ "heat": (BugTask.heat, []),
413+ "latest_patch_uploaded": (Bug.latest_patch_uploaded, []),
414+ "milestone_name": (
415+ Milestone.name,
416+ [
417+ (Milestone,
418+ LeftJoin(Milestone,
419+ BugTask.milestone == Milestone.id))
420+ ]),
421+ "reporter": (
422+ Reporter.name,
423+ [
424+ (Bug, Join(Bug, BugTask.bug == Bug.id)),
425+ (Reporter, Join(Reporter, Bug.owner == Reporter.id))
426+ ]),
427+ "tag": (
428+ BugTag.tag,
429+ [
430+ (Bug, Join(Bug, BugTask.bug == Bug.id)),
431+ (BugTag,
432+ LeftJoin(
433+ BugTag,
434+ BugTag.bug == Bug.id and
435+ # We want at most one tag per bug. Select the
436+ # tag that comes first in alphabetic order.
437+ BugTag.id == SQL("""
438+ SELECT id FROM BugTag AS bt
439+ WHERE bt.bug=bug.id ORDER BY bt.name LIMIT 1
440+ """))),
441+ ]
442+ ),
443+ "specification": (
444+ Specification.name,
445+ [
446+ (Bug, Join(Bug, BugTask.bug == Bug.id)),
447+ (Specification,
448+ LeftJoin(
449+ Specification,
450+ # We want at most one specification per bug.
451+ # Select the specification that comes first
452+ # in alphabetic order.
453+ Specification.id == SQL("""
454+ SELECT Specification.id
455+ FROM SpecificationBug
456+ JOIN Specification
457+ ON SpecificationBug.specification=
458+ Specification.id
459+ WHERE SpecificationBug.bug=Bug.id
460+ ORDER BY Specification.name
461+ LIMIT 1
462+ """))),
463+ ]
464+ ),
465+ }
466
467 def _processOrderBy(self, params):
468 """Process the orderby parameter supplied to search().
469@@ -3362,11 +3363,11 @@
470 orderby_arg.append(orderby_col)
471 continue
472 if orderby_col.startswith("-"):
473- col, sort_joins = self.getOrderByColumnDBName(orderby_col[1:])
474+ col, sort_joins = self.orderby_expression[orderby_col[1:]]
475 extra_joins.extend(sort_joins)
476 order_clause = Desc(col)
477 else:
478- col, sort_joins = self.getOrderByColumnDBName(orderby_col)
479+ col, sort_joins = self.orderby_expression[orderby_col]
480 extra_joins.extend(sort_joins)
481 order_clause = col
482 if col in unambiguous_cols:
483
484=== modified file 'lib/lp/bugs/templates/bugtask-macros-tableview.pt'
485--- lib/lp/bugs/templates/bugtask-macros-tableview.pt 2011-12-08 13:23:00 +0000
486+++ lib/lp/bugs/templates/bugtask-macros-tableview.pt 2011-12-15 10:18:27 +0000
487@@ -662,6 +662,7 @@
488 if (Y.Lang.isNull(navigator)){
489 return;
490 }
491+ var sort_keys = LP.cache.sort_keys;
492 var active_sort_key = LP.cache.order_by;
493 var sort_order = 'asc';
494 if (active_sort_key.charAt(0) === '-') {
495@@ -669,22 +670,26 @@
496 1, active_sort_key.length);
497 sort_order = 'desc';
498 }
499+ var unknown_sort_key = true;
500+ /*xxxxxxxxxxxxxxx
501+ for (var i = 0; i < sort_keys.length; i++) {
502+ if (sort_keys[i][0] === active_sort_key) {
503+ unknown_sort_key = false;
504+ break;
505+ }
506+ }
507+ */
508+ Y.each(sort_keys, function(sort_key) {
509+ if (sort_key[0] === active_sort_key) {
510+ unknown_sort_key = false;
511+ }
512+ });
513+ if (unknown_sort_key) {
514+ active_sort_key = 'importance';
515+ }
516 var orderby = new Y.lp.ordering.OrderByBar({
517 srcNode: Y.one('#bugs-orderby'),
518- sort_keys: [
519- ['id', 'Bug number'],
520- ['title', 'Bug title'],
521- ['importance', 'Importance'],
522- ['status', 'Status'],
523- ['heat', 'Bug heat'],
524- ['reporter', 'Reporter'],
525- ['assignee', 'Assignee'],
526- ['targetname', 'Package/Project/Series name'],
527- ['milestone_name', 'Milestone'],
528- ['datecreated', 'Bug age'],
529- ['date_last_updated', 'Date bug last updated'],
530- ['tag', 'Bug Tags']
531- ],
532+ sort_keys: sort_keys,
533 active: active_sort_key,
534 sort_order: sort_order,
535 config_slot: true
536@@ -693,7 +698,8 @@
537 Y.on('orderbybar:sort', function(e) {
538 navigator.first_batch(e);
539 });
540- navigator.get('model').get('history').after(
541+ var model = navigator.get('model');
542+ model.get('history').after(
543 'change', function(e) {
544 Y.lp.buglisting_utils.update_sort_button_visibility(
545 orderby, e.newVal);
546@@ -701,14 +707,22 @@
547 var config_node = orderby.get('config_node');
548 var list_util = new Y.lp.buglisting_utils.BugListingConfigUtil({
549 srcNode: config_node,
550- model: navigator.get('model')
551+ model: model
552 });
553 list_util.render();
554 Y.on('buglisting-config-util:fields-changed', function(e) {
555 navigator.change_fields(list_util.get('field_visibility'));
556 });
557+ var field_visibility =
558+ navigator.get('model').get_field_visibility();
559+ // The advanced search page contains sort options that have
560+ // no related data fields we can display. If a user has selected
561+ // such a sort order, this sort option should always be visible.
562+ if (field_visibility['show_' + active_sort_key] === undefined) {
563+ orderby.always_display = active_sort_key;
564+ }
565 Y.lp.buglisting_utils.update_sort_button_visibility(
566- orderby, navigator.get('model').get_field_visibility());
567+ orderby, field_visibility);
568 });
569 });
570 </script>