Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Added Extension Sorting capability according to downloads and last pu…
Browse files Browse the repository at this point in the history
…blished date (#13080)

* Added Extension Sorting capability according to downloads and last published date

* Passed sort criteria to sortRegistry function in registy_utils

* Added some tests

* Added check for existence of downloadCount in extension-manager

* Added test for checking downloadCount in ExtensionManager view

* Added svg image for download icon in Extension Manager
  • Loading branch information
saurabh95 authored and swmitra committed Feb 20, 2017
1 parent 8d4c836 commit 2954858
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 44 deletions.
3 changes: 3 additions & 0 deletions src/extensibility/ExtensionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ define(function (require, exports, module) {
_idsToDisable = {};

PreferencesManager.stateManager.definePreference(FOLDER_AUTOINSTALL, "object", undefined);
PreferencesManager.definePreference("extensions.sort", "string", "publishedDate", {
description: Strings.SORT_EXTENSION_METHOD
});

/**
* @private
Expand Down
25 changes: 24 additions & 1 deletion src/extensibility/ExtensionManagerDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ define(function (require, exports, module) {
KeyEvent = require("utils/KeyEvent"),
ExtensionManager = require("extensibility/ExtensionManager"),
ExtensionManagerView = require("extensibility/ExtensionManagerView").ExtensionManagerView,
ExtensionManagerViewModel = require("extensibility/ExtensionManagerViewModel");
ExtensionManagerViewModel = require("extensibility/ExtensionManagerViewModel"),
PreferencesManager = require("preferences/PreferencesManager");

var dialogTemplate = require("text!htmlContent/extension-manager-dialog.html");

Expand Down Expand Up @@ -372,6 +373,11 @@ define(function (require, exports, module) {
if (models[_activeTabIndex]) {
$modalDlg.scrollTop(models[_activeTabIndex].scrollPos || 0);
clearSearch();
if (_activeTabIndex === 2) {
$(".ext-sort-group").hide();
} else {
$(".ext-sort-group").show();
}
}
}

Expand Down Expand Up @@ -460,6 +466,18 @@ define(function (require, exports, module) {
$modalDlg.scrollTop(0);
});
}).on("click", ".search-clear", clearSearch);

// Sort the extension list based on the current selected sorting criteria
$dlg.on("change", ".sort-extensions", function (e) {
var sortBy = $(this).val();
PreferencesManager.set("extensions.sort", sortBy);
models.forEach(function (model, index) {
if (index <= 1) {
model._setSortedExtensionList(ExtensionManager.extensions, index === 1);
views[index].filter($(".search").val());
}
});
});

// Disable the search field when there are no items in the model
models.forEach(function (model, index) {
Expand All @@ -480,6 +498,11 @@ define(function (require, exports, module) {
} else { // Otherwise show the first tab
$dlg.find(".nav-tabs a:first").tab("show");
}
if ($activeTab.hasClass("installed")) {
$(".ext-sort-group").hide();
} else {
$(".ext-sort-group").show();
}
});

// Handle the 'Install from URL' button.
Expand Down
5 changes: 4 additions & 1 deletion src/extensibility/ExtensionManagerView.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ define(function (require, exports, module) {
LanguageManager = require("language/LanguageManager"),
Mustache = require("thirdparty/mustache/mustache"),
PathUtils = require("thirdparty/path-utils/path-utils"),
itemTemplate = require("text!htmlContent/extension-manager-view-item.html");
itemTemplate = require("text!htmlContent/extension-manager-view-item.html"),
PreferencesManager = require("preferences/PreferencesManager");


/**
Expand Down Expand Up @@ -71,6 +72,7 @@ define(function (require, exports, module) {
this._$infoMessage = $("<div class='info-message'/>")
.appendTo(this.$el).html(this.model.infoMessage);
this._$table = $("<table class='table'/>").appendTo(this.$el);
$(".sort-extensions").val(PreferencesManager.get("extensions.sort"));

this.model.initialize().done(function () {
self._setupEventHandlers();
Expand Down Expand Up @@ -248,6 +250,7 @@ define(function (require, exports, module) {
var installWarningBase = context.requiresNewer ? Strings.EXTENSION_LATEST_INCOMPATIBLE_NEWER : Strings.EXTENSION_LATEST_INCOMPATIBLE_OLDER;
context.installWarning = StringUtils.format(installWarningBase, entry.registryInfo.versions[entry.registryInfo.versions.length - 1].version, latestVerCompatInfo.compatibleVersion);
}
context.downloadCount = entry.registryInfo.totalDownloads;
} else {
// We should only get here when viewing the Installed tab and some extensions don't exist in the registry
// (or registry is offline). These flags *should* always be ignored in that scenario, but just in case...
Expand Down
41 changes: 22 additions & 19 deletions src/extensibility/ExtensionManagerViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ define(function (require, exports, module) {

var _ = require("thirdparty/lodash");

var ExtensionManager = require("extensibility/ExtensionManager"),
registry_utils = require("extensibility/registry_utils"),
EventDispatcher = require("utils/EventDispatcher"),
Strings = require("strings");
var ExtensionManager = require("extensibility/ExtensionManager"),
registry_utils = require("extensibility/registry_utils"),
EventDispatcher = require("utils/EventDispatcher"),
Strings = require("strings"),
PreferencesManager = require("preferences/PreferencesManager");

/**
* @private
Expand Down Expand Up @@ -292,6 +293,20 @@ define(function (require, exports, module) {
});
};

ExtensionManagerViewModel.prototype._setSortedExtensionList = function (extensions, isTheme) {
this.filterSet = this.sortedFullSet = registry_utils.sortRegistry(extensions, "registryInfo", PreferencesManager.get("extensions.sort"))
.filter(function (entry) {
if (!isTheme) {
return entry.registryInfo && !entry.registryInfo.metadata.theme;
} else {
return entry.registryInfo && entry.registryInfo.metadata.theme;
}
})
.map(function (entry) {
return entry.registryInfo.metadata.name;
});
};

/**
* The model for the ExtensionManagerView that is responsible for handling registry-based extensions.
* This extends ExtensionManagerViewModel.
Expand Down Expand Up @@ -329,13 +344,7 @@ define(function (require, exports, module) {
self.extensions = ExtensionManager.extensions;

// Sort the registry by last published date and store the sorted list of IDs.
self.sortedFullSet = registry_utils.sortRegistry(self.extensions, "registryInfo")
.filter(function (entry) {
return entry.registryInfo !== undefined && entry.registryInfo.metadata.theme === undefined;
})
.map(function (entry) {
return entry.registryInfo.metadata.name;
});
self._setSortedExtensionList(ExtensionManager.extensions, false);
self._setInitialFilter();
})
.fail(function () {
Expand Down Expand Up @@ -536,13 +545,7 @@ define(function (require, exports, module) {
self.extensions = ExtensionManager.extensions;

// Sort the registry by last published date and store the sorted list of IDs.
self.sortedFullSet = registry_utils.sortRegistry(self.extensions, "registryInfo")
.filter(function (entry) {
return entry.registryInfo !== undefined && entry.registryInfo.metadata.theme;
})
.map(function (entry) {
return entry.registryInfo.metadata.name;
});
self._setSortedExtensionList(ExtensionManager.extensions, true);
self._setInitialFilter();
})
.fail(function () {
Expand Down Expand Up @@ -570,4 +573,4 @@ define(function (require, exports, module) {
exports.RegistryViewModel = RegistryViewModel;
exports.ThemesViewModel = ThemesViewModel;
exports.InstalledViewModel = InstalledViewModel;
});
});
14 changes: 11 additions & 3 deletions src/extensibility/registry_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ define(function (require, exports, module) {
* we should look at the top level of the object.
* @return {Array} Sorted array of registry entries.
*/
exports.sortRegistry = function (registry, subkey) {
exports.sortRegistry = function (registry, subkey, sortBy) {
function getPublishTime(entry) {
if (entry.versions) {
return new Date(entry.versions[entry.versions.length - 1].published).getTime();
Expand All @@ -136,8 +136,16 @@ define(function (require, exports, module) {
sortedEntries.push(registry[key]);
});
sortedEntries.sort(function (entry1, entry2) {
return getPublishTime((subkey && entry2[subkey]) || entry2) -
getPublishTime((subkey && entry1[subkey]) || entry1);
if (sortBy !== "publishedDate") {
if (entry1.registryInfo && entry2.registryInfo) {
return entry2.registryInfo.totalDownloads - entry1.registryInfo.totalDownloads;
} else {
return Number.NEGATIVE_INFINITY;
}
} else {
return getPublishTime((subkey && entry2[subkey]) || entry2) -
getPublishTime((subkey && entry1[subkey]) || entry1);
}
});

return sortedEntries;
Expand Down
5 changes: 5 additions & 0 deletions src/htmlContent/extension-manager-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<li><a href="#installed" class="installed" data-toggle="tab"><span class="notification"></span><img src="styles/images/extension-manager-installed.svg"/><br/>{{Strings.EXTENSIONS_INSTALLED_TITLE}}</a></li>
</ul>
<div>
<span class="sort-extensions-title ext-sort-group">{{Strings.EXTENSIONS_SORT_BY}}</span>
<select class="sort-extensions ext-sort-group">
<option value="publishedDate">{{Strings.EXTENSIONS_LAST_UPDATED}}</option>
<option value="downloadCount">{{Strings.EXTENSIONS_DOWNLOADS}}</option>
</select>
<button class="search-clear">&times;</button>
<input class="search" type="text" placeholder="{{EXTENSION_SEARCH_PLACEHOLDER}}">
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/htmlContent/extension-manager-view-item.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
{{#hasVersionInfo}}
<span class="muted ext-date"> &mdash; {{lastVersionDate}}</span>
{{/hasVersionInfo}}
{{#downloadCount}}
<p title="{{downloadCount}} {{Strings.EXTENSIONS_DOWNLOADS}}"><img src="styles/images/download-icon.svg"/> {{downloadCount}}</p>
{{/downloadCount}}
</td>
<td class="ext-desc">
{{#showInstallButton}}
Expand Down Expand Up @@ -99,4 +102,4 @@
{{/isInstalled}}
</div>
</td>
</tr>
</tr>
4 changes: 4 additions & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ define({
"INSTALL_CANCELED" : "Installation canceled.",
"VIEW_COMPLETE_DESCRIPTION" : "View complete description",
"VIEW_TRUNCATED_DESCRIPTION" : "View truncated description",
"SORT_EXTENSION_METHOD" : "Sort Extensions using downloadCount or publishedDate",
// These must match the error codes in ExtensionsDomain.Errors.* :
"INVALID_ZIP_FILE" : "The downloaded content is not a valid zip file.",
"MISSING_PACKAGE_JSON" : "The package has no package.json file.",
Expand Down Expand Up @@ -580,6 +581,9 @@ define({
"EXTENSIONS_AVAILABLE_TITLE" : "Available",
"EXTENSIONS_THEMES_TITLE" : "Themes",
"EXTENSIONS_UPDATES_TITLE" : "Updates",
"EXTENSIONS_SORT_BY" : "Sort By",
"EXTENSIONS_LAST_UPDATED" : "Last Updated",
"EXTENSIONS_DOWNLOADS" : "Downloads",

"INLINE_EDITOR_NO_MATCHES" : "No matches available.",
"INLINE_EDITOR_HIDDEN_MATCHES" : "All matches are collapsed. Expand the files listed at right to view matches.",
Expand Down
11 changes: 11 additions & 0 deletions src/styles/brackets_patterns_override.less
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,17 @@ a[href^="http"] {
opacity: 0.3;
}
}
.sort-extensions-title {
float: left;
margin-right: 5px;
margin-top: 5px;
}
.sort-extensions {
float: left;
margin-right: 10px;
width: auto;
padding-right: 18px;
}
}
.modal-body {
height: 400px;
Expand Down
19 changes: 19 additions & 0 deletions src/styles/images/download-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 20 additions & 10 deletions test/spec/ExtensionManager-test-files/mockRegistry.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"published": "2013-04-10T18:21:27.058Z",
"brackets": ">0.20.0"
}
]
],
"totalDownloads": 10
},
"long-desc-extension": {
"metadata": {
Expand All @@ -42,7 +43,8 @@
"version": "1.0.0",
"published": "2013-04-10T18:26:20.553Z"
}
]
],
"totalDownloads": 184
},
"mock-extension-1": {
"metadata": {
Expand All @@ -65,7 +67,8 @@
"version": "1.0.0",
"published": "2013-04-11T18:26:20.553Z"
}
]
],
"totalDownloads": 190
},
"mock-extension-2": {
"metadata": {
Expand All @@ -79,7 +82,8 @@
"version": "1.0.0",
"published": "2013-04-11T18:26:20.553Z"
}
]
],
"totalDownloads": 88
},
"mock-extension-3": {
"metadata": {
Expand All @@ -93,7 +97,8 @@
"version": "1.0.0",
"published": "2013-04-11T18:26:20.553Z"
}
]
],
"totalDownloads": 34
},
"mock-extension-4": {
"metadata": {
Expand All @@ -107,7 +112,8 @@
"version": "1.0.0",
"published": "2013-04-11T18:26:20.553Z"
}
]
],
"totalDownloads": 20
},
"malicious-script-extension": {
"metadata": {
Expand All @@ -122,7 +128,8 @@
"version": "1.0.0",
"published": "2013-04-10T18:26:24.956Z"
}
]
],
"totalDownloads": 781
},
"select-parent": {
"metadata": {
Expand All @@ -147,7 +154,8 @@
"published": "2013-04-10T18:26:47.580Z",
"brackets": ">0.22.0"
}
]
],
"totalDownloads": 101
},
"basic-valid-extension": {
"metadata": {
Expand All @@ -165,7 +173,8 @@
"version": "3.0.0",
"published": "2013-04-10T18:29:11.907Z"
}
]
],
"totalDownloads": 130
},
"everyscrub": {
"metadata": {
Expand All @@ -179,6 +188,7 @@
"version": "0.23.0",
"published": "2013-04-11T17:22:38.033Z"
}
]
],
"totalDownloads": 101
}
}
Loading

0 comments on commit 2954858

Please sign in to comment.