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

Commit 9784ba2

Browse files
committed
Support disabling/enabling extensions in UI.
1 parent 314d7ff commit 9784ba2

File tree

7 files changed

+588
-81
lines changed

7 files changed

+588
-81
lines changed

src/extensibility/ExtensionManager.js

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ define(function (require, exports, module) {
104104
/**
105105
* Requested changes to the installed extensions.
106106
*/
107-
var _idsToRemove = [],
108-
_idsToUpdate = [];
107+
var _idsToRemove = {},
108+
_idsToUpdate = {},
109+
_idsToDisable = {};
109110

110111
PreferencesManager.stateManager.definePreference(FOLDER_AUTOINSTALL, "object", undefined);
111112

@@ -187,8 +188,9 @@ define(function (require, exports, module) {
187188
*/
188189
function _reset() {
189190
exports.extensions = extensions = {};
190-
_idsToRemove = [];
191-
_idsToUpdate = [];
191+
_idsToRemove = {};
192+
_idsToUpdate = {};
193+
_idsToDisable = {};
192194
}
193195

194196
/**
@@ -267,7 +269,7 @@ define(function (require, exports, module) {
267269
metadata: metadata,
268270
path: path,
269271
locationType: locationType,
270-
status: (e.type === "loadFailed" ? START_FAILED : (metadata.disabled ? DISABLED : ENABLED))
272+
status: (e.type === "loadFailed" ? START_FAILED : (e.type === "disabled" ? DISABLED : ENABLED))
271273
};
272274

273275
synchronizeEntry(id);
@@ -406,6 +408,60 @@ define(function (require, exports, module) {
406408
}
407409
return result.promise();
408410
}
411+
412+
/**
413+
* Disables the installed extension with the given id.
414+
*
415+
* @param {string} id The id of the extension to disable.
416+
* @return {$.Promise} A promise that's resolved when the extenion is disabled or
417+
* rejected with an error that prevented the disabling.
418+
*/
419+
function disable(id) {
420+
var result = new $.Deferred(),
421+
extension = extensions[id];
422+
if (extension && extension.installInfo) {
423+
Package.disable(extension.installInfo.path)
424+
.done(function () {
425+
extension.installInfo.status = DISABLED;
426+
extension.installInfo.metadata.disabled = true;
427+
result.resolve();
428+
exports.trigger("statusChange", id);
429+
})
430+
.fail(function (err) {
431+
result.reject(err);
432+
});
433+
} else {
434+
result.reject(StringUtils.format(Strings.EXTENSION_NOT_INSTALLED, id));
435+
}
436+
return result.promise();
437+
}
438+
439+
/**
440+
* Enables the installed extension with the given id.
441+
*
442+
* @param {string} id The id of the extension to enable.
443+
* @return {$.Promise} A promise that's resolved when the extenion is enabled or
444+
* rejected with an error that prevented the enabling.
445+
*/
446+
function enable(id) {
447+
var result = new $.Deferred(),
448+
extension = extensions[id];
449+
if (extension && extension.installInfo) {
450+
Package.enable(extension.installInfo.path)
451+
.done(function () {
452+
extension.installInfo.status = ENABLED;
453+
extension.installInfo.metadata.disabled = false;
454+
result.resolve();
455+
exports.trigger("statusChange", id);
456+
})
457+
.fail(function (err) {
458+
result.reject(err);
459+
});
460+
} else {
461+
result.reject(StringUtils.format(Strings.EXTENSION_NOT_INSTALLED, id));
462+
}
463+
return result.promise();
464+
}
409465

410466
/**
411467
* Updates an installed extension with the given package file.
@@ -460,7 +516,7 @@ define(function (require, exports, module) {
460516
}
461517
exports.trigger("statusChange", id);
462518
}
463-
519+
464520
/**
465521
* Returns true if an extension is marked for removal.
466522
* @param {string} id The id of the extension to check.
@@ -469,7 +525,7 @@ define(function (require, exports, module) {
469525
function isMarkedForRemoval(id) {
470526
return !!(_idsToRemove[id]);
471527
}
472-
528+
473529
/**
474530
* Returns true if there are any extensions marked for removal.
475531
* @return {boolean} true if there are extensions to remove
@@ -478,6 +534,39 @@ define(function (require, exports, module) {
478534
return Object.keys(_idsToRemove).length > 0;
479535
}
480536

537+
/**
538+
* Marks an extension for disabling later, or unmarks an extension previously marked.
539+
*
540+
* @param {string} id The id of the extension
541+
* @param {boolean} mark Whether to mark or unmark the extension.
542+
*/
543+
function markForDisabling(id, mark) {
544+
if (mark) {
545+
_idsToDisable[id] = true;
546+
} else {
547+
delete _idsToDisable[id];
548+
}
549+
exports.trigger("statusChange", id);
550+
}
551+
552+
/**
553+
* Returns true if an extension is mark for disabling.
554+
*
555+
* @param {string} id The id of the extension to check.
556+
* @return {boolean} true if it's been mark for disabling, false otherwise.
557+
*/
558+
function isMarkedForDisabling(id) {
559+
return !!(_idsToDisable[id]);
560+
}
561+
562+
/**
563+
* Returns true if there are any extensions marked for disabling.
564+
* @return {boolean} true if there are extensions to disable
565+
*/
566+
function hasExtensionsToDisable() {
567+
return Object.keys(_idsToDisable).length > 0;
568+
}
569+
481570
/**
482571
* If a downloaded package appears to be an update, mark the extension for update.
483572
* If an extension was previously marked for removal, marking for update will
@@ -550,6 +639,25 @@ define(function (require, exports, module) {
550639
}
551640
);
552641
}
642+
643+
/**
644+
* Disables extensions marked for disabling.
645+
*
646+
* If the return promise is rejected, the argument will contain an array of objects. Each
647+
* element is an object identifying the extension failed with "item" property set to the
648+
* extension id which has failed to be disabled and "error" property set to the error.
649+
*
650+
* @return {$.Promise} A promise that's resolved when all extensions marked for disabling are
651+
* disabled or rejected if one or more extensions can't be disabled.
652+
*/
653+
function disableMarkedExtensions() {
654+
return Async.doInParallel_aggregateErrors(
655+
Object.keys(_idsToDisable),
656+
function (id) {
657+
return disable(id);
658+
}
659+
);
660+
}
553661

554662
/**
555663
* Updates extensions previously marked for update.
@@ -784,24 +892,31 @@ define(function (require, exports, module) {
784892
exports.getExtensionURL = getExtensionURL;
785893
exports.remove = remove;
786894
exports.update = update;
895+
exports.disable = disable;
896+
exports.enable = enable;
787897
exports.extensions = extensions;
788898
exports.cleanupUpdates = cleanupUpdates;
789899
exports.markForRemoval = markForRemoval;
790900
exports.isMarkedForRemoval = isMarkedForRemoval;
791901
exports.unmarkAllForRemoval = unmarkAllForRemoval;
792902
exports.hasExtensionsToRemove = hasExtensionsToRemove;
903+
exports.markForDisabling = markForDisabling;
904+
exports.isMarkedForDisabling = isMarkedForDisabling;
905+
exports.hasExtensionsToDisable = hasExtensionsToDisable;
793906
exports.updateFromDownload = updateFromDownload;
794907
exports.removeUpdate = removeUpdate;
795908
exports.isMarkedForUpdate = isMarkedForUpdate;
796909
exports.hasExtensionsToUpdate = hasExtensionsToUpdate;
797910
exports.removeMarkedExtensions = removeMarkedExtensions;
911+
exports.disableMarkedExtensions = disableMarkedExtensions;
798912
exports.updateExtensions = updateExtensions;
799913
exports.getAvailableUpdates = getAvailableUpdates;
800914
exports.cleanAvailableUpdates = cleanAvailableUpdates;
801915

802916
exports.hasDownloadedRegistry = false;
803917

804918
exports.ENABLED = ENABLED;
919+
exports.DISABLED = DISABLED;
805920
exports.START_FAILED = START_FAILED;
806921

807922
exports.LOCATION_DEFAULT = LOCATION_DEFAULT;

src/extensibility/ExtensionManagerDialog.js

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,20 @@ define(function (require, exports, module) {
6363
*/
6464
function _performChanges() {
6565
// If an extension was removed or updated, prompt the user to quit Brackets.
66-
var hasRemovedExtensions = ExtensionManager.hasExtensionsToRemove(),
67-
hasUpdatedExtensions = ExtensionManager.hasExtensionsToUpdate();
68-
if (!hasRemovedExtensions && !hasUpdatedExtensions) {
66+
var hasRemovedExtensions = ExtensionManager.hasExtensionsToRemove(),
67+
hasUpdatedExtensions = ExtensionManager.hasExtensionsToUpdate(),
68+
hasDisabledExtensions = ExtensionManager.hasExtensionsToDisable();
69+
if (!hasRemovedExtensions && !hasUpdatedExtensions && !hasDisabledExtensions) {
6970
return;
7071
}
7172

7273
var buttonLabel = Strings.CHANGE_AND_RELOAD;
73-
if (hasRemovedExtensions && !hasUpdatedExtensions) {
74+
if (hasRemovedExtensions && !hasUpdatedExtensions && !hasDisabledExtensions) {
7475
buttonLabel = Strings.REMOVE_AND_RELOAD;
75-
} else if (hasUpdatedExtensions && !hasRemovedExtensions) {
76+
} else if (hasUpdatedExtensions && !hasRemovedExtensions && !hasDisabledExtensions) {
7677
buttonLabel = Strings.UPDATE_AND_RELOAD;
78+
} else if (hasDisabledExtensions && !hasRemovedExtensions && !hasUpdatedExtensions) {
79+
buttonLabel = Strings.DISABLE_AND_RELOAD;
7780
}
7881

7982
var dlg = Dialogs.showModalDialog(
@@ -107,57 +110,98 @@ define(function (require, exports, module) {
107110
.text(Strings.PROCESSING_EXTENSIONS)
108111
.append("<span class='spinner inline spin'/>");
109112

110-
ExtensionManager.removeMarkedExtensions()
113+
var removeExtensionsPromise,
114+
updateExtensionsPromise,
115+
disableExtensionsPromise,
116+
removeErrors,
117+
updateErrors,
118+
disableErrors;
119+
120+
removeExtensionsPromise = ExtensionManager.removeMarkedExtensions();
121+
removeExtensionsPromise
122+
.fail(function (errorArray) {
123+
removeErrors = errorArray;
124+
});
125+
updateExtensionsPromise = ExtensionManager.updateExtensions();
126+
updateExtensionsPromise
127+
.fail(function (errorArray) {
128+
updateErrors = errorArray;
129+
});
130+
disableExtensionsPromise = ExtensionManager.disableMarkedExtensions();
131+
disableExtensionsPromise
132+
.fail(function (errorArray) {
133+
disableErrors = errorArray;
134+
});
135+
136+
Async.waitForAll([removeExtensionsPromise, updateExtensionsPromise, disableExtensionsPromise])
137+
.always(function () {
138+
dlg.close();
139+
})
111140
.done(function () {
112-
ExtensionManager.updateExtensions()
113-
.done(function () {
114-
dlg.close();
141+
CommandManager.execute(Commands.APP_RELOAD);
142+
})
143+
.fail(function () {
144+
var ids = [],
145+
dialogs = [];
146+
147+
function nextDialog() {
148+
var dialog = dialogs.shift();
149+
if (dialog) {
150+
Dialogs.showModalDialog(dialog.dialog, dialog.title, dialog.message)
151+
.done(nextDialog);
152+
} else {
153+
// Even in case of error condition, we still have to reload
115154
CommandManager.execute(Commands.APP_RELOAD);
116-
})
117-
.fail(function (errorArray) {
118-
dlg.close();
119-
120-
// This error case should be very uncommon.
121-
// Just let the user know that we couldn't update
122-
// this extension and log the errors to the console.
123-
var ids = [];
124-
errorArray.forEach(function (errorObj) {
125-
ids.push(errorObj.item);
126-
if (errorObj.error && errorObj.error.forEach) {
127-
console.error("Errors for", errorObj.item);
128-
errorObj.error.forEach(function (error) {
129-
console.error(Package.formatError(error));
130-
});
131-
} else {
132-
console.error("Error for", errorObj.item, errorObj);
133-
}
134-
});
135-
Dialogs.showModalDialog(
136-
DefaultDialogs.DIALOG_ID_ERROR,
137-
Strings.EXTENSION_MANAGER_UPDATE,
138-
StringUtils.format(Strings.EXTENSION_MANAGER_UPDATE_ERROR, ids.join(", "))
139-
).done(function () {
140-
// We still have to reload even if some of the removals failed.
141-
CommandManager.execute(Commands.APP_RELOAD);
142-
});
155+
}
156+
}
157+
158+
if (removeErrors) {
159+
removeErrors.forEach(function (errorObj) {
160+
ids.push(errorObj.item);
143161
});
144-
})
145-
.fail(function (errorArray) {
146-
dlg.close();
147-
ExtensionManager.cleanupUpdates();
162+
dialogs.push({
163+
dialog: DefaultDialogs.DIALOG_ID_ERROR,
164+
title: Strings.EXTENSION_MANAGER_REMOVE,
165+
message: StringUtils.format(Strings.EXTENSION_MANAGER_REMOVE_ERROR, ids.join(", "))
166+
});
167+
}
148168

149-
var ids = [];
150-
errorArray.forEach(function (errorObj) {
151-
ids.push(errorObj.item);
152-
});
153-
Dialogs.showModalDialog(
154-
DefaultDialogs.DIALOG_ID_ERROR,
155-
Strings.EXTENSION_MANAGER_REMOVE,
156-
StringUtils.format(Strings.EXTENSION_MANAGER_REMOVE_ERROR, ids.join(", "))
157-
).done(function () {
158-
// We still have to reload even if some of the removals failed.
159-
CommandManager.execute(Commands.APP_RELOAD);
160-
});
169+
if (updateErrors) {
170+
// This error case should be very uncommon.
171+
// Just let the user know that we couldn't update
172+
// this extension and log the errors to the console.
173+
ids.length = 0;
174+
updateErrors.forEach(function (errorObj) {
175+
ids.push(errorObj.item);
176+
if (errorObj.error && errorObj.error.forEach) {
177+
console.error("Errors for", errorObj.item);
178+
errorObj.error.forEach(function (error) {
179+
console.error(Package.formatError(error));
180+
});
181+
} else {
182+
console.error("Error for", errorObj.item, errorObj);
183+
}
184+
});
185+
dialogs.push({
186+
dialog: DefaultDialogs.DIALOG_ID_ERROR,
187+
title: Strings.EXTENSION_MANAGER_UPDATE,
188+
message: StringUtils.format(Strings.EXTENSION_MANAGER_UPDATE_ERROR, ids.join(", "))
189+
});
190+
}
191+
192+
if (disableErrors) {
193+
ids.length = 0;
194+
disableErrors.forEach(function (errorObj) {
195+
ids.push(errorObj.item);
196+
});
197+
dialogs.push({
198+
dialog: DefaultDialogs.DIALOG_ID_ERROR,
199+
title: Strings.EXTENSION_MANAGER_REMOVE,
200+
message: StringUtils.format(Strings.EXTENSION_MANAGER_REMOVE_ERROR, ids.join(", "))
201+
});
202+
}
203+
204+
nextDialog();
161205
});
162206
} else {
163207
dlg.close();

0 commit comments

Comments
 (0)