/** * // Avoid server side code : // https://github.com/ipython/ipython/issues/2780 * * This essentially boils down to the following: * Github authentication requires some server-side code for any 'app' which * wants to authenticate over the Github API. * When registering an app with Github, Github provides the app with what they * call a 'client secret'. * The client secret is then incorporated into the app, and the app sends it to * Github as part of the authentication process, thus proving to Github's * servers that the communicating app was written by someone with appropriate * credentials. * * The issue with writing a single Github app for Gist-ing notebooks, is that * it would need to include such a client secret. Since this would be part of * the extension source code, anyone could use the client secret, potentially * gaining the permissions that any given user has granted to the app. * * As a result, we only support: * - anonymous (non-authenticated) API usage * - client-side authentication using a personal access token * (see https://github.com/settings/tokens) */ define([ 'jquery', 'base/js/namespace', 'base/js/dialog' ], function ( $, Jupyter, dialog ) { "use strict"; // define default values for config parameters var params = { gist_it_default_to_public: false, gist_it_personal_access_token: '', github_endpoint: 'github.com' }; var initialize = function () { update_params(); Jupyter.toolbar.add_buttons_group([ Jupyter.keyboard_manager.actions.register ({ help : 'Create/Edit Gist of Notebook', icon : 'fa-github', handler: show_gist_editor_modal }, 'create-gist-from-notebook', 'gist_it') ]); }; // update params with any specified in the server's config file var update_params = function() { var config = Jupyter.notebook.config; for (var key in params) { if (config.data.hasOwnProperty(key)) params[key] = config.data[key]; } default_metadata.data.public = Boolean(config.data.gist_it_default_to_public); }; var default_metadata = { id: '', data: { description: Jupyter.notebook.notebook_path, public: false } }; function ensure_default_metadata () { Jupyter.notebook.metadata.gist = $.extend( true, // deep-copy default_metadata, //defaults Jupyter.notebook.metadata.gist // overrides ); } var add_auth_token = function add_auth_token (xhr) { var token = ''; if (params.gist_it_personal_access_token !== '') { token = params.gist_it_personal_access_token; } if (token !== '') { xhr.setRequestHeader("Authorization", "token " + token); } }; function build_alert(alert_class) { return $('
') .addClass('alert alert-dismissable') .addClass(alert_class) .append( $('') .append($('').html('×')) ); } function gist_error (jqXHR, textStatus, errorThrown) { console.log('github ajax error:', jqXHR, textStatus, errorThrown); var alert = build_alert('alert-danger') .hide() .append( $('').text('The ajax request to Github went wrong:') ) .append( $('').text(jqXHR.responseJSON ? JSON.stringify(jqXHR.responseJSON, null, 2) : errorThrown) ); $('#gist_modal').find('.modal-body').append(alert); alert.slideDown('fast'); } function gist_success (response, textStatus, jqXHR) { // if (Jupyter.notebook.metadata.gist.id === response.id) return; Jupyter.notebook.metadata.gist.id = response.id; Jupyter.notebook.metadata._draft = $.extend( true, // deep copy Jupyter.notebook.metadata._draft, // defaults {nbviewer_url: response.html_url} // overrides ); var d = new Date(); var msg_head = d.toLocaleString() + ': Gist '; var msg_tail = response.history.length === 1 ? ' published' : ' updated to revision ' + response.history.length; var alert = build_alert('alert-success') .hide() .append(msg_head) .append( $('') .attr('href', response.html_url) .attr('target', '_blank') .text(response.id) ) .append(msg_tail); $('#gist_modal').find('.modal-body').append(alert); alert.slideDown('fast'); } function get_github_endpoint() { return params.github_endpoint !== '' ? params.github_endpoint : 'github.com'; } function get_api_endpoint() { const github_endpoint = get_github_endpoint(); if (github_endpoint === 'github.com') { return 'https://api.'+ github_endpoint; } else { // Github Enterprise // https://developer.github.com/enterprise/2.18/v3/enterprise-admin/#endpoint-urls return 'https://' + github_endpoint + '/api/v3' } } function gist_id_updated_callback(gist_editor) { if (gist_editor === undefined) gist_editor = $('#gist_editor'); var id_input = gist_editor.find('#gist_id'); var id = id_input.val(); var help_block = gist_editor.find('#gist_id ~ .help-block'); var help_block_base_text = 'Set the gist id to update an existing gist, ' + 'or leave blank to create a new one.'; var gist_it_button = $('#gist_modal').find('.btn-primary'); id_input.parent() .removeClass('has-success has-error has-warning') .find('#gist_id ~ .form-control-feedback > i.fa') .removeClass('fa-pencil-square fa-exclamation-circle fa-question-circle'); if (id === '') { $('#gist_id ~ .form-control-feedback > i.fa') .addClass('fa-plus-circle'); help_block.html( '' + help_block_base_text + '
' + 'a new gist will be created
' ); gist_it_button.prop('disabled', false); } else { $('#gist_id ~ .form-control-feedback > i.fa') .addClass('fa-circle-o-notch fa-spin'); // List commits as a way of checking whether the gist exists. // Listing commits appears to give the most concise response. $.ajax({ url: get_api_endpoint() +'/gists/' + id + '/commits', dataType: 'json', beforeSend: add_auth_token, error: function(jqXHR, textStatus, errorThrown) { jqXHR.errorThrown = errorThrown; }, complete: function(jqXHR, textStatus) { var success = textStatus === 'success'; var error = !success && jqXHR.status === 404 && jqXHR.responseJSON !== undefined; var warning = !success && !error; var help_block_html = '' + help_block_base_text + '
'; gist_it_button.prop('disabled', error); if (success) { var single = (jqXHR.responseJSON.length === 1); help_block_html += '' + '' + ' gist ' + '' + id + ' will be updated' + ' (' + jqXHR.responseJSON.length + ' revision' + (single ? '' : 's') + ' exist' + (single ? 's' : '') + ' so far)' + '
'; } else if (error) { help_block_html += '' + '' + ' no gist exists with the specified id (given current access token)'+ '
'; } else { help_block_html += '' + '' + ' can\'t list commits for the specified gist id - you may have problems updating it!' + '
'; help_block_html += 'The ajax request to Github went wrong:
' + ''; if (jqXHR.responseJSON) { help_block_html += JSON.stringify(jqXHR.responseJSON, null, 2); } else { help_block_html += jqXHR.errorThrown || textStatus; } help_block_html += ''; console.log('non-404 github ajax error:', jqXHR, textStatus); } help_block.html(help_block_html); id_input.parent() .toggleClass('has-success', success) .toggleClass('has-error', error) .toggleClass('has-warning', warning) .find('#gist_id ~ .form-control-feedback > i.fa') .removeClass('fa-circle-o-notch fa-spin') .toggleClass('fa-pencil-square', success) .toggleClass('fa-exclamation-circle', error) .toggleClass('fa-question-circle', warning); } }); } } function update_gist_editor (gist_editor) { if (gist_editor === undefined) gist_editor = $('#gist_editor'); var id_input = gist_editor.find('#gist_id'); var have_auth = params.gist_it_personal_access_token !== ''; var id = ''; var is_public = true; if (have_auth) { id = Jupyter.notebook.metadata.gist.id; is_public = Jupyter.notebook.metadata.gist.data.public; id_input.val(id); } id_input.closest('.form-group').toggle(have_auth); gist_editor.find('#gist_public') .prop('checked', is_public) .prop('readonly', !have_auth); gist_editor.find('#gist_description') .val(Jupyter.notebook.metadata.gist.data.description); if (have_auth) { gist_id_updated_callback(gist_editor); } } function build_gist_editor () { ensure_default_metadata(); var gist_editor = $('#gist_editor'); if (gist_editor.length > 0) return gist_editor; gist_editor = $('').attr('id', 'gist_editor').append(controls); var id = params.gist_it_personal_access_token !== '' ? Jupyter.notebook.metadata.gist.id : ''; var controls = $('') .appendTo(gist_editor) .addClass('form-horizontal'); $('') .addClass('has-feedback') .hide() .appendTo(controls) .append( $('') .attr('for', 'gist_id') .text('Gist id') ) .append( $('') .addClass('form-control') .attr('id', 'gist_id') .val(Jupyter.notebook.metadata.gist.id) ) .append( $('') .addClass('form-control-feedback') .append( $('') .addClass('fa fa-lg') ) ) .append( $('') .addClass('help-block') ); $('') .appendTo(controls) .append( $('') .addClass('checkbox') .append( $('