Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/extensibility/ExtensionManagerDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ define(function (require, exports, module) {
}

models.push(new ExtensionManagerViewModel.InstalledViewModel());
models.push(new ExtensionManagerViewModel.DefaultViewModel());

function updateSearchDisabled() {
var model = models[_activeTabIndex],
Expand Down
8 changes: 4 additions & 4 deletions src/extensibility/ExtensionManagerView.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,10 @@ define(function (require, exports, module) {

context.removalAllowed = this.model.source === "installed" &&
!context.failedToStart && !hasPendingAction;
context.disablingAllowed = this.model.source === "installed" &&
!context.disabled && !hasPendingAction;
context.enablingAllowed = this.model.source === "installed" &&
context.disabled && !hasPendingAction;
var isDefaultOrInstalled = this.model.source === "default" || this.model.source === "installed";
var isDefaultAndTheme = this.model.source === "default" && context.metadata.theme;
context.disablingAllowed = isDefaultOrInstalled && !isDefaultAndTheme && !context.disabled && !hasPendingAction;
context.enablingAllowed = isDefaultOrInstalled && !isDefaultAndTheme && context.disabled && !hasPendingAction;

// Copy over helper functions that we share with the registry app.
["lastVersionDate", "authorInfo"].forEach(function (helper) {
Expand Down
66 changes: 66 additions & 0 deletions src/extensibility/ExtensionManagerViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ define(function (require, exports, module) {
*/
ExtensionManagerViewModel.prototype.SOURCE_INSTALLED = "installed";

/**
* @type {string}
* Constant indicating that this model/view should initialize from the list of default bundled extensions.
*/
ExtensionManagerViewModel.prototype.SOURCE_DEFAULT = "default";

/**
* @type {Object}
* The current set of extensions managed by this model. Same as ExtensionManager.extensions.
Expand Down Expand Up @@ -509,6 +515,65 @@ define(function (require, exports, module) {
return entry;
};

/**
* Model for displaying default extensions that come bundled with Brackets
*/
function DefaultViewModel() {
ExtensionManagerViewModel.call(this);
}

// Inheritance setup
DefaultViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
DefaultViewModel.prototype.constructor = DefaultViewModel;

/**
* Add SOURCE_DEFAULT to DefaultViewModel
*/
DefaultViewModel.prototype.source = ExtensionManagerViewModel.prototype.SOURCE_DEFAULT;

/**
* Initializes the model from the set of default extensions, sorted alphabetically by id
* @return {$.Promise} a promise that's resolved when we're done initializing.
*/
DefaultViewModel.prototype._initializeFromSource = function () {
var self = this;
this.extensions = ExtensionManager.extensions;
this.sortedFullSet = Object.keys(this.extensions)
.filter(function (key) {
return self.extensions[key].installInfo &&
self.extensions[key].installInfo.locationType === ExtensionManager.LOCATION_DEFAULT;
});
this._sortFullSet();
this._setInitialFilter();
return new $.Deferred().resolve().promise();
};

/**
* @private
* Re-sorts the current full set
*/
DefaultViewModel.prototype._sortFullSet = function () {
var self = this;
this.sortedFullSet = this.sortedFullSet.sort(function (key1, key2) {
var metadata1 = self.extensions[key1].installInfo.metadata,
metadata2 = self.extensions[key2].installInfo.metadata,
id1 = (metadata1.title || metadata1.name).toLocaleLowerCase(),
id2 = (metadata2.title || metadata2.name).toLocaleLowerCase();
return id1.localeCompare(id2);
});
};

/**
* @private
* Finds the default extension metadata by id. If there is no default extension matching the given id,
* this returns `null`.
* @param {string} id of the theme extension
* @return {Object?} extension metadata or null if there's no matching extension
*/
DefaultViewModel.prototype._getEntry = function (id) {
return this.extensions[id] ? this.extensions[id].installInfo : null;
};

/**
* The model for the ExtensionManagerView that is responsible for handling registry-based theme extensions.
* This extends ExtensionManagerViewModel.
Expand Down Expand Up @@ -573,4 +638,5 @@ define(function (require, exports, module) {
exports.RegistryViewModel = RegistryViewModel;
exports.ThemesViewModel = ThemesViewModel;
exports.InstalledViewModel = InstalledViewModel;
exports.DefaultViewModel = DefaultViewModel;
});
56 changes: 48 additions & 8 deletions src/extensibility/Package.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ define(function (require, exports, module) {
description: Strings.DESCRIPTION_PROXY
});

var PREF_EXTENSIONS_DEFAULT_DISABLED = "extensions.default.disabled";
PreferencesManager.definePreference(PREF_EXTENSIONS_DEFAULT_DISABLED, "array", [], {
description: Strings.DESCRIPTION_DISABLED_DEFAULT_EXTENSIONS
});

var Errors = {
ERROR_LOADING: "ERROR_LOADING",
MALFORMED_URL: "MALFORMED_URL",
Expand Down Expand Up @@ -436,6 +441,23 @@ define(function (require, exports, module) {
});
}

/**
* This function manages the PREF_EXTENSIONS_DEFAULT_DISABLED preference
* holding an array of default extension paths that should not be loaded
* on Brackets startup
*/
function toggleDefaultExtension(path, enabled) {
var arr = PreferencesManager.get(PREF_EXTENSIONS_DEFAULT_DISABLED);
if (!Array.isArray(arr)) { arr = []; }
var io = arr.indexOf(path);
if (enabled === true && io !== -1) {
arr.splice(io, 1);
} else if (enabled === false && io === -1) {
arr.push(path);
}
PreferencesManager.set(PREF_EXTENSIONS_DEFAULT_DISABLED, arr);
}

/**
* Disables the extension at the given path.
*
Expand All @@ -446,12 +468,19 @@ define(function (require, exports, module) {
function disable(path) {
var result = new $.Deferred(),
file = FileSystem.getFileForPath(path + "/.disabled");

var defaultExtensionPath = ExtensionLoader.getDefaultExtensionPath();
if (file.fullPath.indexOf(defaultExtensionPath) === 0) {
toggleDefaultExtension(path, false);
result.resolve();
return result.promise();
}

file.write("", function (err) {
if (err) {
result.reject(err);
} else {
result.resolve();
return result.reject(err);
}
result.resolve();
});
return result.promise();
}
Expand All @@ -466,14 +495,25 @@ define(function (require, exports, module) {
function enable(path) {
var result = new $.Deferred(),
file = FileSystem.getFileForPath(path + "/.disabled");
file.unlink(function (err) {
if (err) {
result.reject(err);
return;
}

function afterEnable() {
ExtensionLoader.loadExtension(FileUtils.getBaseName(path), { baseUrl: path }, "main")
.done(result.resolve)
.fail(result.reject);
}

var defaultExtensionPath = ExtensionLoader.getDefaultExtensionPath();
if (file.fullPath.indexOf(defaultExtensionPath) === 0) {
toggleDefaultExtension(path, true);
afterEnable();
return result.promise();
}

file.unlink(function (err) {
if (err) {
return result.reject(err);
}
afterEnable();
});
return result.promise();
}
Expand Down
16 changes: 15 additions & 1 deletion src/htmlContent/extension-manager-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@
<li><a href="#themes" class="themes" data-toggle="tab"><img src="styles/images/themes-icon.svg"/><br/>{{Strings.EXTENSIONS_THEMES_TITLE}}</a></li>
<!--<li><a href="#updates" class="updates" data-toggle="tab"><img src="styles/images/extension-manager-updates.svg"/><br/>{{Strings.EXTENSIONS_UPDATES_TITLE}}</a></li>-->
{{/showRegistry}}
<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>
<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>
<li>
<a href="#default" class="default" data-toggle="tab">
<img src="styles/images/extension-manager-installed.svg"/>
<br/>
{{Strings.EXTENSIONS_DEFAULT_TITLE}}
</a>
</li>
</ul>
<div>
<div class="ext-sort-group">
Expand Down
2 changes: 1 addition & 1 deletion src/htmlContent/extension-manager-view-item.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@
{{/isInstalled}}
</div>
</td>
</tr>
</tr>
2 changes: 2 additions & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ define({
"NO_EXTENSION_MATCHES" : "No extensions match your search.",
"REGISTRY_SANITY_CHECK_WARNING" : "NOTE: These extensions may come from different authors than {APP_NAME} itself. Extensions are not reviewed and have full local privileges. Be cautious when installing extensions from an unknown source.",
"EXTENSIONS_INSTALLED_TITLE" : "Installed",
"EXTENSIONS_DEFAULT_TITLE" : "Default",
"EXTENSIONS_AVAILABLE_TITLE" : "Available",
"EXTENSIONS_THEMES_TITLE" : "Themes",
"EXTENSIONS_UPDATES_TITLE" : "Updates",
Expand Down Expand Up @@ -703,6 +704,7 @@ define({
"DESCRIPTION_CODE_FOLDING_MIN_FOLD_SIZE" : "Minimum lines before a collapsible section icon appears",
"DESCRIPTION_CODE_FOLDING_SAVE_FOLD_STATES" : "true to remember collapsed sections if you close and reopen a file or project",
"DESCRIPTION_CODE_FOLDING_MAKE_SELECTIONS_FOLDABLE": "true to enable code folding on selected text in the editor",
"DESCRIPTION_DISABLED_DEFAULT_EXTENSIONS" : "Default extensions that are disabled",
"DESCRIPTION_ATTR_HINTS" : "Enable/disable HTML attribute hints",
"DESCRIPTION_CSS_PROP_HINTS" : "Enable/disable CSS/LESS/SCSS property hints",
"DESCRIPTION_JS_HINTS" : "Enable/disable JavaScript code hints",
Expand Down
2 changes: 1 addition & 1 deletion src/styles/brackets_patterns_override.less
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ a[href^="http"] {
/* Extension Manager dialog */
.extension-manager-dialog {
background-color: @bc-panel-bg-promoted;
width: 760px;
min-width: @extension-manager-min-width;

.dark & {
background-color: @dark-bc-panel-bg-promoted;
Expand Down
2 changes: 1 addition & 1 deletion src/styles/brackets_variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
@main-toolbar-width: 30px;

/* Extension Manager */
@extension-manager-min-width: 680px; // Used for responsive styling
@extension-manager-min-width: 760px; // Used for responsive styling

/* z-index */
@z-index-cm-dialog-override: 11;
Expand Down
14 changes: 13 additions & 1 deletion src/utils/ExtensionLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ define(function (require, exports, module) {
globalPaths[key] = PathUtils.makePathAbsolute(srcPath + "/" + globalPaths[key]);
});

/**
* Returns the full path to the default extensions directory.
*/
function getDefaultExtensionPath() {
return FileUtils.getNativeBracketsDirectoryPath() + "/extensions/default";
}

/**
* Returns the full path of the default user extensions directory. This is in the users
* application support directory, which is typically
Expand Down Expand Up @@ -405,7 +412,11 @@ define(function (require, exports, module) {
if (params.get("reloadWithoutUserExts") === "true") {
paths = ["default"];
} else {
paths = ["default", "dev", getUserExtensionPath()];
paths = [
getDefaultExtensionPath(),
"dev",
getUserExtensionPath()
];
}
}

Expand Down Expand Up @@ -453,6 +464,7 @@ define(function (require, exports, module) {

// public API
exports.init = init;
exports.getDefaultExtensionPath = getDefaultExtensionPath;
exports.getUserExtensionPath = getUserExtensionPath;
exports.getRequireContextForExtension = getRequireContextForExtension;
exports.loadExtension = loadExtension;
Expand Down
29 changes: 21 additions & 8 deletions src/utils/ExtensionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@
define(function (require, exports, module) {
"use strict";

var Async = require("utils/Async"),
FileSystem = require("filesystem/FileSystem"),
FileUtils = require("file/FileUtils"),
PathUtils = require("thirdparty/path-utils/path-utils");
var Async = require("utils/Async"),
FileSystem = require("filesystem/FileSystem"),
FileUtils = require("file/FileUtils"),
PathUtils = require("thirdparty/path-utils/path-utils"),
PreferencesManager = require("preferences/PreferencesManager");

/**
* Appends a <style> tag to the document's head.
Expand Down Expand Up @@ -240,6 +241,7 @@ define(function (require, exports, module) {
function loadMetadata(folder) {
var packageJSONFile = FileSystem.getFileForPath(folder + "/package.json"),
disabledFile = FileSystem.getFileForPath(folder + "/.disabled"),
baseName = FileUtils.getBaseName(folder),
result = new $.Deferred(),
jsonPromise = new $.Deferred(),
disabledPromise = new $.Deferred(),
Expand All @@ -261,16 +263,27 @@ define(function (require, exports, module) {
} else {
disabled = exists;
}

var defaultDisabled = PreferencesManager.get("extensions.default.disabled");
if (Array.isArray(defaultDisabled) && defaultDisabled.indexOf(folder) !== -1) {
console.warn("Default extension has been disabled on startup: " + baseName);
disabled = true;
}

disabledPromise.resolve();
});
Async.waitForAll([jsonPromise, disabledPromise])
.always(function () {
if (!json) {
result.reject(disabled);
} else {
json.disabled = disabled;
result.resolve(json);
// if we don't have any metadata for the extension
// we should still create an empty one, so we can attach
// disabled property on it in case it's disabled
json = {
name: baseName
};
}
json.disabled = disabled;
result.resolve(json);
});
return result.promise();
}
Expand Down