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

Drag and drop local zip file install for ExtensionManager #8166

Merged
merged 8 commits into from
Jul 21, 2014

Conversation

jasonsanjose
Copy link
Member

Adds drag and drop support to the ExtensionManagerDialog to allow users to install extensions via local zip files from the native file system. The rough workflow is below:

  1. Open extension manager
  2. Drag and drop zip from Explorer/Finder onto extension manager dialog
  3. Open InstallExtensionDialog
  4. Validate the zip is an extension
  5. Check if the zip should be installed or updated
  6. Install

FYI I noticed #8165 while debugging this branch. It also appears on master.

This new workflow allows users to experiment with extensions not yet delivered via the Extension Registry and that are also not available via a public URL.

Here's a related trello story I found https://trello.com/c/fIGcd4V3.

promise;

// TODO validate version?
if (isUpdate) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dangoor I wasn't sure if I should be checking versions here to block users from installing older versions. Compatibility with brackets API version is handled elsewhere right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we currently do any version/compatibility checking when using the manual "Install from URL" workflow. Maybe we should, though :-) @jasonsanjose do you think it's something that's critical for our use case here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked in ExtensionDomain.js. We do check compatibility on installation:

        if (validationResult.metadata && validationResult.metadata.engines &&
                validationResult.metadata.engines.brackets) {
            var compatible = semver.satisfies(options.apiVersion,
                                              validationResult.metadata.engines.brackets);
            if (!compatible) {
                installDirectory = path.join(options.disabledDirectory, extensionName);
                validationResult.installationStatus = Statuses.DISABLED;
                validationResult.disabledReason = Errors.API_NOT_COMPATIBLE;
                _removeAndInstall(packagePath, installDirectory, validationResult, deleteTempAndCallback);
                return;
            }
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's API compatibility that we check. I don't think we block installation of older versions anywhere. If someone wants to install an older version of an extension and it's compatible, we don't stop them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! That works for us.

@jasonsanjose
Copy link
Member Author

It would be great to see this land in the next sprint to help us as we move towards a prerelease.

ExtensionManagerView.prototype._installUsingDragAndDrop = function () {
var self = this;

brackets.app.getDroppedFiles(function (err, files) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I was a bit confused as to why we can't just keep looking at event.originalEvent.dataTransfer.files (we do it this way in brackets.js for the general drag/drop case too)... but upon further inspection I think it's because the W3C event never provides you the full, local path (since you can't normally do anything useful with it). @RaymondLim is that right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. This is why we have to rely on shell code to provide the file paths.

@peterflynn
Copy link
Member

Marking triage complete. I added a few questions/comments, but the overall approach looks solid to me. And this will be useful for users who are stuck behind a firewall, etc. too (better than our existing manual install workflow).

@peterflynn
Copy link
Member

Oh, one other question: should we add anything to the UI indicating that Extension Manager is a 'drop zone'? Otherwise this is totally undiscoverable... though otoh I can't think of anything offhand that doesn't clutter up the UI. Thoughts, @larz0?

@jasonsanjose
Copy link
Member Author

Implemented UI from @larz0:

image

image

@jasonsanjose
Copy link
Member Author

@peterflynn ready for review

* @return {$.Promise} A promise that's resolved when the extension is updated or
* rejected with an error if there's a problem with the update.
*/
function update(id, packagePath) {
return Package.installUpdate(packagePath, id);
function update(id, packagePath, keepFile) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to add a keepFile flag so that we don't automatically cleanup local zip files from drag-and-drop.

@larz0
Copy link
Member

larz0 commented Jun 20, 2014

@jasonsanjose UI looks good!

var extension = FileUtils.getFileExtension(path);

if (err || !file.isFile || (extension !== "zip")) {
result.reject();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's considered bad style to reject without a proper error cause.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@dangoor dangoor added the Review label Jul 17, 2014
@dangoor dangoor self-assigned this Jul 17, 2014
// new install or an update.
Package.validate(path).then(function (info) {
if (info.errors.length) {
result.reject();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto what @ingorichter says above. If the user drops an invalid package here, we should tell them what's wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@dangoor
Copy link
Contributor

dangoor commented Jul 17, 2014

I worry a little about the lack of tests for this because I wouldn't want to see the keepFile flag get missed somewhere along the way and then we start deleting dragged in files.

@dangoor
Copy link
Contributor

dangoor commented Jul 17, 2014

One thing I noticed in testing: if I drag in an image file and place it where the zip should go, Brackets ends up opening the image behind the Extension Manager dialog.

@dangoor
Copy link
Contributor

dangoor commented Jul 17, 2014

Works pretty well when you drop in a valid zip file. For multiple zip files, the experience is not quite as good because the install dialog isn't really set up to install multiple files nicely. Ideal case would be something more like a download manager that lists everything that's installing and shows the status of each. Even listing which extension "installed successfully" may be an improvement when installing multiple.

But, installing multiple is something of an edge case, so I don't think we need to do anything as far as that goes right now.

* localized error message.
*
* @param {string} path Absolute path to the package zip file
* @param {?string} nameHint Hint for the extension folder's name (used in favor of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: jsdoc argument name doesn't match the actual one filenameHint

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@dangoor
Copy link
Contributor

dangoor commented Jul 17, 2014

Review done (finally! I've been hopping back and forth between things all day). I did test with a package that had an invalid value in package.json and this just fails silently. Again, that's an edge case... but a quick blink of drop zone is all I saw, and I can see someone being given a broken test package and then filing a bug saying "package installation doesn't work!"

To summarize:

  • Display an errors that come up in the package validation/install
  • Consider displaying which extension installed correctly in the install succeeded dialog
  • Have a test that verifies that the file remains after installation with keepFile being true
  • Couple of nits

This is a very useful feature! Thanks for building this, Jason!

@peterflynn
Copy link
Member

@dangoor @jasonsanjose Would it be simpler to turn off support for dropping multiple zip files? I'm not sure how ugly it currently looks when you do that, or how much complexity it adds to the code, but we could easily just no-op or show an error when the number of dropped files != 1... And I don't think we have an urgent use case for dropping multiple files at this point...

@dangoor
Copy link
Contributor

dangoor commented Jul 18, 2014

@peterflynn It's not super ugly. It's basically just one modal install dialog after another, and it goes quickly since the files are local. There's a little complexity added, but it's mostly just a couple of calls to Async.doSequentially.

I'm overall pretty neutral on the support of multiple files.

@jasonsanjose
Copy link
Member Author

Thanks for the review. I'm off today but I can respond on Monday. When is this sprint locking down?

@dangoor
Copy link
Contributor

dangoor commented Jul 18, 2014

Monday would be the earliest. We can shift by a day or two if needed.

@jasonsanjose
Copy link
Member Author

Here's the new error dialog when validation errors occur. Notes:

  • installation errors will be handled by the InstallExtensionDialog
  • Errors are formatted by Package.formatError (this example just happens to be "unknown")

image

@ryanstewart
Copy link
Contributor

Is "Unknown internal error" just a placeholder? Or does that error come up a lot?

If so, should we think about not showing the error message?

@jasonsanjose
Copy link
Member Author

Is "Unknown internal error" just a placeholder? Or does that error come up a lot?
If so, should we think about not showing the error message?

It's only possible in a few cases where we don't have error strings, see https://github.com/adobe/brackets/blob/master/src/extensibility/node/package-validator.js#L42.

This is not new behavior. I just happened to be testing a weird case where package.json was missing (which we don't have a string for).

@ryanstewart
Copy link
Contributor

Cool, sounds good!

@jasonsanjose
Copy link
Member Author

From @dangoor's list

  • [FIXED] Display an errors that come up in the package validation/install
  • [FILE BUG] Consider displaying which extension installed correctly in the install succeeded dialog
  • [TODO] Have a test that verifies that the file remains after installation with keepFile being true
  • [FIXED] Couple of nits

One thing I noticed in testing: if I drag in an image file and place it where the zip should go, Brackets ends up opening the image behind the Extension Manager dialog.

Fixed.

I'll have unit tests updated soon.

@marcelgerber
Copy link
Contributor

Well, I guess it's reasonable to have an error message for extensions missing a package.json, right?

@peterflynn
Copy link
Member

@SAplayer In the usual case -- installing from extension registry -- that's not possible, so it's sort of an edge case. That said, I'm sure the extension registry itself has strings for package format errors like that, so it shouldn't be hard to copy them over if we want... just doesn't seem that urgent.

@marcelgerber
Copy link
Contributor

Well, we've got an "main.js missing" string too, which the registry won't allow either, I guess.
Just for consistency :) (and it may be a little more likely with locally installed extensions)

@peterflynn
Copy link
Member

@SAplayer Do you want to file a bug so we can track adding more error strings? (I don't think it should block this PR)

@dangoor
Copy link
Contributor

dangoor commented Jul 21, 2014

@jasonsanjose This looks good. I think we can merge this and add the test in a follow up.

dangoor added a commit that referenced this pull request Jul 21, 2014
…aganddrop

Drag and drop local zip file install for ExtensionManager
@dangoor dangoor merged commit 46bc7e6 into master Jul 21, 2014
@dangoor
Copy link
Contributor

dangoor commented Jul 21, 2014

Merging but keeping the branch open for the test.

@jasonsanjose
Copy link
Member Author

Actually. I'll just delete this branch and sync to master anyway. Thanks!

@jasonsanjose jasonsanjose deleted the jasonsanjose/extension-install-draganddrop branch July 21, 2014 20:11
@le717
Copy link
Contributor

le717 commented Jul 21, 2014

@jasonsanjose Thank you! This is bound to be a helpful feature. :D

I just noticed one thing, not sure if it was intentional or not. When you hover over the link, the cursor remains at cursor: default. I was expecting it to change to cursor: pointer like all the other links. Like I said, this might have been intentional.

@ryanstewart
Copy link
Contributor

Sweet! Thanks to everyone for getting this merged. Can't wait to be able to use this.

@jasonsanjose
Copy link
Member Author

Thanks for catching the cursor problem. I'll fix that tonight.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants