commit a452af6c3a3a2f007510fbb8bd507ccc65c1177f
parent 984789d304179a75edbe5d0e185d0fd88da0cbe7
Author: Dan Stillman <dstillman@zotero.org>
Date: Tue, 21 Jul 2015 00:44:38 -0400
Sort descendant collections alphabetically in advanced search window
https://forums.zotero.org/discussion/50679/
Diffstat:
3 files changed, 146 insertions(+), 126 deletions(-)
diff --git a/chrome/content/zotero/bindings/zoterosearch.xml b/chrome/content/zotero/bindings/zoterosearch.xml
@@ -405,130 +405,130 @@
]]>
</constructor>
<method name="onConditionSelected">
- <body>
- <![CDATA[
- // Skip if correct condition already selected
- if (this.id('conditionsmenu').value==this.selectedCondition){
- return;
- }
- var conditionsMenu = this.id('conditionsmenu');
- var operatorsList = this.id('operatorsmenu');
-
- this.selectedCondition = conditionsMenu.value;
- this.selectedOperator = operatorsList.value;
-
- var condition = Zotero.SearchConditions.get(conditionsMenu.value);
- var operators = condition['operators'];
-
- // Display appropriate operators for condition
- var selectThis;
- for(var i = 0, len = operatorsList.firstChild.childNodes.length; i < len; i++)
- {
- var val = operatorsList.firstChild.childNodes[i].getAttribute('value');
- var hidden = !operators[val];
- operatorsList.firstChild.childNodes[i].setAttribute('hidden', hidden);
- if (!hidden && (selectThis == null || this.selectedOperator == val))
+ <body><![CDATA[
+ return Zotero.spawn(function* () {
+ // Skip if correct condition already selected
+ if (this.id('conditionsmenu').value==this.selectedCondition){
+ return;
+ }
+ var conditionsMenu = this.id('conditionsmenu');
+ var operatorsList = this.id('operatorsmenu');
+
+ this.selectedCondition = conditionsMenu.value;
+ this.selectedOperator = operatorsList.value;
+
+ var condition = Zotero.SearchConditions.get(conditionsMenu.value);
+ var operators = condition['operators'];
+
+ // Display appropriate operators for condition
+ var selectThis;
+ for(var i = 0, len = operatorsList.firstChild.childNodes.length; i < len; i++)
{
- selectThis = i;
+ var val = operatorsList.firstChild.childNodes[i].getAttribute('value');
+ var hidden = !operators[val];
+ operatorsList.firstChild.childNodes[i].setAttribute('hidden', hidden);
+ if (!hidden && (selectThis == null || this.selectedOperator == val))
+ {
+ selectThis = i;
+ }
}
- }
- operatorsList.selectedIndex = selectThis;
-
- // Generate drop-down menu instead of textbox for certain conditions
- switch (conditionsMenu.value){
- case 'collection':
- var rows = [];
-
- var libraryID = this.parent.search.libraryID;
- var cols = Zotero.getCollections(false, true, libraryID); // TODO: Replace with Zotero.Collections.getByLibrary()
- for (var i in cols) {
- // Indent subcollections
- var indent = '';
- if (cols[i].level) {
- for (var j=1; j<cols[i].level; j++) {
- indent += ' ';
+ operatorsList.selectedIndex = selectThis;
+
+ // Generate drop-down menu instead of textbox for certain conditions
+ switch (conditionsMenu.value){
+ case 'collection':
+ var rows = [];
+
+ var libraryID = this.parent.search.libraryID;
+ var cols = yield Zotero.Collections.getByLibrary(libraryID, true);
+ for (var i in cols) {
+ // Indent subcollections
+ var indent = '';
+ if (cols[i].level) {
+ for (var j=1; j<cols[i].level; j++) {
+ indent += ' ';
+ }
+ indent += '- ';
}
- indent += '- ';
+ rows.push([indent + cols[i].name, 'C' + cols[i].key]);
}
- rows.push([indent + cols[i].name, 'C' + cols[i].key]);
- }
- this.createValueMenu(rows);
- break;
-
- case 'savedSearch':
- var rows = [];
- var libraryID = this.parent.search.libraryID;
- var searches = Zotero.Searches.getAll(libraryID);
- for (var i in searches) {
- if (searches[i].id != this.parent.search.id) {
- rows.push([searches[i].name, 'S' + searches[i].key]);
+ this.createValueMenu(rows);
+ break;
+
+ case 'savedSearch':
+ var rows = [];
+ var libraryID = this.parent.search.libraryID;
+ var searches = Zotero.Searches.getAll(libraryID);
+ for (var i in searches) {
+ if (searches[i].id != this.parent.search.id) {
+ rows.push([searches[i].name, 'S' + searches[i].key]);
+ }
}
- }
- this.createValueMenu(rows);
- break;
-
- case 'itemType':
- var t = Zotero.ItemTypes.getTypes();
+ this.createValueMenu(rows);
+ break;
- // Sort by localized name
- var types = [];
- for (var i=0; i<t.length; i++) {
- types.push({
- id: t[i].id,
- name: t[i].name,
- localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
+ case 'itemType':
+ var t = Zotero.ItemTypes.getTypes();
+
+ // Sort by localized name
+ var types = [];
+ for (var i=0; i<t.length; i++) {
+ types.push({
+ id: t[i].id,
+ name: t[i].name,
+ localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
+ });
+ }
+ var collation = Zotero.getLocaleCollation();
+ types.sort(function(a, b) {
+ return collation.compareString(1, a.localized, b.localized);
});
- }
- var collation = Zotero.getLocaleCollation();
- types.sort(function(a, b) {
- return collation.compareString(1, a.localized, b.localized);
- });
+
+ for (var i in types) {
+ types[i][0] = types[i].localized;
+ types[i][1] = types[i].name;
+ delete types[i]['name'];
+ delete types[i]['id'];
+ }
+ this.createValueMenu(types);
+ break;
- for (var i in types) {
- types[i][0] = types[i].localized;
- types[i][1] = types[i].name;
- delete types[i]['name'];
- delete types[i]['id'];
- }
- this.createValueMenu(types);
- break;
-
- case 'fileTypeID':
- var types = Zotero.FileTypes.getTypes();
- for (var i in types) {
- types[i][0] = Zotero.getString('fileTypes.' + types[i]['name']);
- types[i][1] = types[i]['id'];
- delete types[i]['name'];
- delete types[i]['id'];
- }
- this.createValueMenu(types);
- break;
-
- default:
- if (operatorsList.value=='isInTheLast')
- {
- this.id('value-date-age').value = this.value;
- }
+ case 'fileTypeID':
+ var types = Zotero.FileTypes.getTypes();
+ for (var i in types) {
+ types[i][0] = Zotero.getString('fileTypes.' + types[i]['name']);
+ types[i][1] = types[i]['id'];
+ delete types[i]['name'];
+ delete types[i]['id'];
+ }
+ this.createValueMenu(types);
+ break;
- // Textbox
- else {
- // If switching from menu to textbox, clear value
- if (this.id('valuefield').hidden){
- this.id('valuefield').value = '';
+ default:
+ if (operatorsList.value=='isInTheLast')
+ {
+ this.id('value-date-age').value = this.value;
}
- // If switching between textbox conditions, get loaded value for new one
+
+ // Textbox
else {
- this.id('valuefield').value = this.value;
+ // If switching from menu to textbox, clear value
+ if (this.id('valuefield').hidden){
+ this.id('valuefield').value = '';
+ }
+ // If switching between textbox conditions, get loaded value for new one
+ else {
+ this.id('valuefield').value = this.value;
+ }
+
+ // Update field drop-down if applicable
+ this.id('valuefield').update(conditionsMenu.value, this.mode);
}
-
- // Update field drop-down if applicable
- this.id('valuefield').update(conditionsMenu.value, this.mode);
- }
- }
-
- this.onOperatorSelected();
- ]]>
- </body>
+ }
+
+ this.onOperatorSelected();
+ }.bind(this));
+ ]]></body>
</method>
<method name="onOperatorSelected">
<body>
@@ -597,8 +597,8 @@
<method name="initWithParentAndCondition">
<parameter name="parent"/>
<parameter name="condition"/>
- <body>
- <![CDATA[
+ <body><![CDATA[
+ return Zotero.spawn(function* () {
this.parent = parent;
this.conditionID = condition['id'];
@@ -638,11 +638,11 @@
this.dontupdate = false;
}
- this.onConditionSelected();
+ yield this.onConditionSelected();
this.id('conditionsmenu').focus();
- ]]>
- </body>
+ }.bind(this));
+ ]]></body>
</method>
<!-- Gets the value from the field and updates the associated search condition -->
<method name="updateSearch">
diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js
@@ -690,17 +690,21 @@ Zotero.Collection.prototype.getChildren = Zotero.Promise.coroutine(function* (re
// 0 == collection
// 1 == item
- var sql = 'SELECT collectionID AS id, '
+ var sql = 'SELECT collectionID AS id, collectionName AS name, '
+ "0 AS type, collectionName AS collectionName, key "
+ 'FROM collections WHERE parentCollectionID=?1';
if (!type || type == 'item') {
- sql += ' UNION SELECT itemID AS id, 1 AS type, NULL AS collectionName, key '
+ sql += ' UNION SELECT itemID AS id, NULL AS name, 1 AS type, NULL AS collectionName, key '
+ 'FROM collectionItems JOIN items USING (itemID) WHERE collectionID=?1';
if (!includeDeletedItems) {
sql += " AND itemID NOT IN (SELECT itemID FROM deletedItems)";
}
}
var children = yield Zotero.DB.queryAsync(sql, this.id);
+ children.sort(function (a, b) {
+ if (a.name === null || b.name === null) return 0;
+ return Zotero.localeCompare(a.name, b.name)
+ });
for(var i=0, len=children.length; i<len; i++) {
// This seems to not work without parseInt() even though
diff --git a/test/tests/collectionsTest.js b/test/tests/collectionsTest.js
@@ -13,13 +13,29 @@ describe("Zotero.Collections", function () {
})
it("should get all collections in a library in recursive mode", function* () {
- var col1 = yield createDataObject('collection');
- var col2 = yield createDataObject('collection');
- var col3 = yield createDataObject('collection', { parentID: col2.id });
- var cols = yield Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID, true);
- assert.isAbove(cols.length, 2);
- assert.includeMembers(cols.map(col => col.id), [col1.id, col2.id, col3.id]);
- assert.ok(cols.every(col => col.libraryID == Zotero.Libraries.userLibraryID));
+ yield createDataObject('collection', { libraryID: (yield getGroup()).libraryID });
+
+ var libraryID = Zotero.Libraries.userLibraryID;
+ var col1 = yield createDataObject('collection', { name: "C" });
+ var col2 = yield createDataObject('collection', { name: "A" });
+ var col3 = yield createDataObject('collection', { name: "D", parentID: col2.id });
+ var col4 = yield createDataObject('collection', { name: "B", parentID: col2.id });
+ var col5 = yield createDataObject('collection', { name: "E", parentID: col2.id });
+ var col6 = yield createDataObject('collection', { name: "G", parentID: col3.id });
+ var col7 = yield createDataObject('collection', { name: "F", parentID: col3.id });
+ var cols = yield Zotero.Collections.getByLibrary(libraryID, true);
+ assert.isAbove(cols.length, 6);
+ var ids = cols.map(col => col.id);
+ assert.includeMembers(
+ ids, [col1.id, col2.id, col3.id, col4.id, col5.id, col6.id, col7.id]
+ );
+ assert.isBelow(ids.indexOf(col2.id), ids.indexOf(col4.id), "A before child B");
+ assert.isBelow(ids.indexOf(col4.id), ids.indexOf(col3.id), "B before D");
+ assert.isBelow(ids.indexOf(col3.id), ids.indexOf(col7.id), "D before child F");
+ assert.isBelow(ids.indexOf(col7.id), ids.indexOf(col6.id), "F before G");
+ assert.isBelow(ids.indexOf(col6.id), ids.indexOf(col5.id), "G before D sibling E");
+ assert.isBelow(ids.indexOf(col5.id), ids.indexOf(col1.id), "E before A sibling C");
+ assert.ok(cols.every(col => col.libraryID == libraryID));
})
})