From ad66040d91518aeabf0d576ef43cceabd885f75d Mon Sep 17 00:00:00 2001 From: Mariusz Jurowicz Date: Fri, 19 Jan 2018 15:17:02 +0100 Subject: [PATCH] #6594 allow to publish personal gists (#6637) * #6586 show error dialog on gist publishing error * #6586 move gistPublish to extension dir * #66586 show the network error in gist publishing dialog * #6594 allow to publish personal gists * #6594 use html file for modal template * #6594 store personal access token in beakerx.json * #6594 add oauth scope information to modal * adjust text * can press enter * #6594 allow publish on enter click * #6594 set publish as default button --- js/notebook/src/Plot.js | 2 +- js/notebook/src/extension.js | 2 +- .../extension/gistPublish/gistPublishModal.ts | 103 ++++++++++++++++++ .../{GistPublish.ts => gistPublish/index.ts} | 43 ++++---- .../extension/gistPublish/modalTemplate.html | 36 ++++++ js/notebook/src/plot/plotScope.js | 2 +- 6 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 js/notebook/src/extension/gistPublish/gistPublishModal.ts rename js/notebook/src/extension/{GistPublish.ts => gistPublish/index.ts} (77%) create mode 100644 js/notebook/src/extension/gistPublish/modalTemplate.html diff --git a/js/notebook/src/Plot.js b/js/notebook/src/Plot.js index 7b272ba151..0ff1162941 100644 --- a/js/notebook/src/Plot.js +++ b/js/notebook/src/Plot.js @@ -75,7 +75,7 @@ var PlotView = widgets.DOMWidgetView.extend({ scope.destroy(); }); } - that._currentScope.destroy(); + that._currentScope && that._currentScope.destroy(); setTimeout(function() { that._currentScope = null; }); }); diff --git a/js/notebook/src/extension.js b/js/notebook/src/extension.js index 98f7a21bf3..d76b7d78fe 100644 --- a/js/notebook/src/extension.js +++ b/js/notebook/src/extension.js @@ -49,7 +49,7 @@ define([ './plot/plotApi', './shared/bkCoreManager', 'big.js', - './extension/GistPublish' + './extension/gistPublish/index' ], function( configmod, comm, diff --git a/js/notebook/src/extension/gistPublish/gistPublishModal.ts b/js/notebook/src/extension/gistPublish/gistPublishModal.ts new file mode 100644 index 0000000000..c9c82627ab --- /dev/null +++ b/js/notebook/src/extension/gistPublish/gistPublishModal.ts @@ -0,0 +1,103 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as _ from 'underscore'; + +const dialog = require('base/js/dialog'); +const utils = require('base/js/utils'); + +export default class GistPublishModal { + static template = _.template(require('./modalTemplate.html'))(); + static settingsUrl = `${(Jupyter.notebook_list || Jupyter.notebook).base_url}beakerx/settings`; + + static show(submitCallback: Function): void { + GistPublishModal.getGithubPersonalAccessToken() + .then(personalAccessToken => { + GistPublishModal.create(submitCallback, personalAccessToken); + }); + } + + static create(submitCallback, personalAccessToken = '') { + const modalContent = GistPublishModal.createModalContent(); + const personalAccessTokenInput = modalContent.querySelector('input'); + const form = modalContent.querySelector('form'); + + const submitHandler = (event) => { + const personalAccessToken = personalAccessTokenInput ? personalAccessTokenInput.value : ''; + + event.preventDefault(); + submitCallback(personalAccessToken); + GistPublishModal.storePersonalAccessToken(personalAccessToken); + }; + + if (personalAccessTokenInput && form) { + personalAccessTokenInput.value = personalAccessToken; + } + + const modal = dialog.modal({ + keyboard_manager: Jupyter.notebook.keyboard_manager, + title : 'Publish to a GitHub Gist', + body : modalContent, + default_button: 'Publish', + buttons: { + 'Publish': { + 'class' : 'btn-primary', + 'click': submitHandler + }, + 'Cancel': {} + } + }); + + if (form) { + form.onsubmit = (event) => { + modal.modal('hide'); + submitHandler(event); + } + } + } + + static createModalContent(): HTMLElement { + const modalContent = document.createElement('div'); + + modalContent.innerHTML = GistPublishModal.template; + + return modalContent; + } + + static storePersonalAccessToken(githubPersonalAccessToken = ''): Promise { + return GistPublishModal.getStoredSettings() + .then(storedSettings => + utils.ajax(GistPublishModal.settingsUrl, { + type: 'POST', + data: JSON.stringify({ + ...storedSettings, + githubPersonalAccessToken + }) + }).fail(reason => { console.log(reason); }) + ); + } + + static getGithubPersonalAccessToken(): Promise { + return GistPublishModal.getStoredSettings() + .then(settings => settings.githubPersonalAccessToken || ''); + } + + static getStoredSettings(): Promise { + return utils.ajax(GistPublishModal.settingsUrl, { + method: 'GET' + }).fail(reason => { console.log(reason); }); + } +} diff --git a/js/notebook/src/extension/GistPublish.ts b/js/notebook/src/extension/gistPublish/index.ts similarity index 77% rename from js/notebook/src/extension/GistPublish.ts rename to js/notebook/src/extension/gistPublish/index.ts index bb03b96e09..a28542e18c 100644 --- a/js/notebook/src/extension/GistPublish.ts +++ b/js/notebook/src/extension/gistPublish/index.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import $ from 'jquery'; +import * as $ from 'jquery'; +import GistPublishModal from './gistPublishModal'; const dialog = require('base/js/dialog'); const CONFIG = { @@ -44,19 +45,9 @@ export function registerFeature(): void { } function beforePublish(): void { - dialog.modal({ - title : 'Publish', - body : 'Publish to an anonymous Github Gist, and open in nbviewer?', - buttons: { - 'OK': { - 'class' : 'btn-primary', - 'click': function() { - saveWidgetsState().then(doPublish); - } - }, - 'Cancel': {} - } - }); + GistPublishModal.show(personalAccessToken => saveWidgetsState().then( + () => doPublish(personalAccessToken) + )); } function showErrorDialog(errorMsg) { @@ -84,7 +75,7 @@ function saveWidgetsState(): Promise { }); } -function doPublish(): void { +function doPublish(personalAccessToken): void { const nbjson = Jupyter.notebook.toJSON(); const filedata = {}; @@ -92,6 +83,11 @@ function doPublish(): void { content : JSON.stringify(nbjson, undefined, 1) }; + let gistsUrl = CONFIG.gistsUrl; + if (personalAccessToken) { + gistsUrl = `${gistsUrl}?oauth_token=${personalAccessToken}`; + } + const settings = { type : 'POST', headers : {}, @@ -102,14 +98,17 @@ function doPublish(): void { success : (data, status) => { console.log("gist successfully published: " + data.id); window.open(CONFIG.nbviewerBaseUrl + data.id); - }, - error : (jqXHR, status, err) => { - const errorMsg = jqXHR.readyState === 0 && !err ? 'NETWORK ERROR!' : err; - - console.log(errorMsg); - showErrorDialog(errorMsg); } }; - $.ajax(CONFIG.gistsUrl, settings); + return $.ajax(gistsUrl, settings).catch((jqXHR, status, err) => { + let errorMsg = jqXHR.readyState === 0 && !err ? 'NETWORK ERROR!' : err; + + if (jqXHR.responseJSON && jqXHR.responseJSON.message) { + errorMsg = jqXHR.responseJSON.message; + } + + console.log(errorMsg); + showErrorDialog(errorMsg); + }); } diff --git a/js/notebook/src/extension/gistPublish/modalTemplate.html b/js/notebook/src/extension/gistPublish/modalTemplate.html new file mode 100644 index 0000000000..cc7a9a0005 --- /dev/null +++ b/js/notebook/src/extension/gistPublish/modalTemplate.html @@ -0,0 +1,36 @@ + + +
+

+ Leave the Personal Access Token field empty to publish as an anonymous gist. +
+ Press enter or click the "Publish" button below. A + window will open with the results, which you can share like + any URL. +

+
+
+ + +
+
+

+ Enter a Personal Access Token to publish the notebook as a gist in your GitHub account.
+ We recommend your Personal Access Token have only the gists scope.
+ You can read about scopes here +

+
diff --git a/js/notebook/src/plot/plotScope.js b/js/notebook/src/plot/plotScope.js index 135af5bc04..423c10832f 100644 --- a/js/notebook/src/plot/plotScope.js +++ b/js/notebook/src/plot/plotScope.js @@ -48,7 +48,7 @@ define([ moment ) { - var CONTEXT_MENU_DEBOUNCE_TIME = 250; + var CONTEXT_MENU_DEBOUNCE_TIME = 350; function PlotScope(wrapperId) { this.wrapperId = wrapperId;