Removing google drive integration
It's sad but it's not working. For multiple releases this should be already broken which shows how often it's used. As there is also a security issue related to that, it's better to remove the feature completely. Whoever wants to rewrite it, feel free to go. This commit removes the Google Drive integration from HackMD's Frontend editor and this way removes the need to provide any API key and Client ID in the frontend. Signed-off-by: Sheogorath <sheogorath@shivering-isles.com>
This commit is contained in:
parent
b8e7c4b97a
commit
ad69c5017b
11 changed files with 3 additions and 516 deletions
|
@ -272,7 +272,7 @@ There are some config settings you need to change in the files below.
|
|||
| ------- | --------- | ----------- |
|
||||
| facebook, twitter, github, gitlab, mattermost, dropbox, google, ldap, saml | environment variables or `config.json` | for signin |
|
||||
| imgur, s3, minio | environment variables or `config.json` | for image upload |
|
||||
| google drive(`google/apiKey`, `google/clientID`), dropbox(`dropbox/appKey`) | `config.json` | for export and import |
|
||||
| dropbox(`dropbox/appKey`) | `config.json` | for export and import |
|
||||
|
||||
## Third-party integration OAuth callback URLs
|
||||
|
||||
|
|
2
app.js
2
app.js
|
@ -33,8 +33,6 @@ var data = {
|
|||
urlpath: config.urlPath,
|
||||
debug: config.debug,
|
||||
version: config.version,
|
||||
GOOGLE_API_KEY: config.google.clientSecret,
|
||||
GOOGLE_CLIENT_ID: config.google.clientID,
|
||||
DROPBOX_APP_KEY: config.dropbox.appKey,
|
||||
allowedUploadMimeTypes: config.allowedUploadMimeTypes
|
||||
}
|
||||
|
|
4
app.json
4
app.json
|
@ -136,10 +136,6 @@
|
|||
"description": "Google API client secret",
|
||||
"required": false
|
||||
},
|
||||
"HMD_GOOGLE_API_KEY": {
|
||||
"description": "Google API key (for import/export)",
|
||||
"required": false
|
||||
},
|
||||
"HMD_IMGUR_CLIENTID": {
|
||||
"description": "Imgur API client id",
|
||||
"required": false
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
/** !
|
||||
* Google Drive File Picker Example
|
||||
* By Daniel Lo Nigro (http://dan.cx/)
|
||||
*/
|
||||
(function () {
|
||||
/**
|
||||
* Initialise a Google Driver file picker
|
||||
*/
|
||||
var FilePicker = window.FilePicker = function (options) {
|
||||
// Config
|
||||
this.apiKey = options.apiKey
|
||||
this.clientId = options.clientId
|
||||
|
||||
// Elements
|
||||
this.buttonEl = options.buttonEl
|
||||
|
||||
// Events
|
||||
this.onSelect = options.onSelect
|
||||
this.buttonEl.on('click', this.open.bind(this))
|
||||
|
||||
// Disable the button until the API loads, as it won't work properly until then.
|
||||
this.buttonEl.prop('disabled', true)
|
||||
|
||||
// Load the drive API
|
||||
window.gapi.client.setApiKey(this.apiKey)
|
||||
window.gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this))
|
||||
window.google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) })
|
||||
}
|
||||
|
||||
FilePicker.prototype = {
|
||||
/**
|
||||
* Open the file picker.
|
||||
*/
|
||||
open: function () {
|
||||
// Check if the user has already authenticated
|
||||
var token = window.gapi.auth.getToken()
|
||||
if (token) {
|
||||
this._showPicker()
|
||||
} else {
|
||||
// The user has not yet authenticated with Google
|
||||
// We need to do the authentication before displaying the Drive picker.
|
||||
this._doAuth(false, function () { this._showPicker() }.bind(this))
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the file picker once authentication has been done.
|
||||
* @private
|
||||
*/
|
||||
_showPicker: function () {
|
||||
var accessToken = window.gapi.auth.getToken().access_token
|
||||
var view = new window.google.picker.DocsView()
|
||||
view.setMimeTypes('text/markdown,text/html')
|
||||
view.setIncludeFolders(true)
|
||||
view.setOwnedByMe(true)
|
||||
this.picker = new window.google.picker.PickerBuilder()
|
||||
.enableFeature(window.google.picker.Feature.NAV_HIDDEN)
|
||||
.addView(view)
|
||||
.setAppId(this.clientId)
|
||||
.setOAuthToken(accessToken)
|
||||
.setCallback(this._pickerCallback.bind(this))
|
||||
.build()
|
||||
.setVisible(true)
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a file has been selected in the Google Drive file picker.
|
||||
* @private
|
||||
*/
|
||||
_pickerCallback: function (data) {
|
||||
if (data[window.google.picker.Response.ACTION] === window.google.picker.Action.PICKED) {
|
||||
var file = data[window.google.picker.Response.DOCUMENTS][0]
|
||||
var id = file[window.google.picker.Document.ID]
|
||||
var request = window.gapi.client.drive.files.get({
|
||||
fileId: id
|
||||
})
|
||||
request.execute(this._fileGetCallback.bind(this))
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Called when file details have been retrieved from Google Drive.
|
||||
* @private
|
||||
*/
|
||||
_fileGetCallback: function (file) {
|
||||
if (this.onSelect) {
|
||||
this.onSelect(file)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the Google Drive file picker API has finished loading.
|
||||
* @private
|
||||
*/
|
||||
_pickerApiLoaded: function () {
|
||||
this.buttonEl.prop('disabled', false)
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the Google Drive API has finished loading.
|
||||
* @private
|
||||
*/
|
||||
_driveApiLoaded: function () {
|
||||
this._doAuth(true)
|
||||
},
|
||||
|
||||
/**
|
||||
* Authenticate with Google Drive via the Google JavaScript API.
|
||||
* @private
|
||||
*/
|
||||
_doAuth: function (immediate, callback) {
|
||||
window.gapi.auth.authorize({
|
||||
client_id: this.clientId,
|
||||
scope: 'https://www.googleapis.com/auth/drive.readonly',
|
||||
immediate: immediate
|
||||
}, callback || function () {})
|
||||
}
|
||||
}
|
||||
}())
|
|
@ -1,267 +0,0 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/**
|
||||
* Helper for implementing retries with backoff. Initial retry
|
||||
* delay is 1 second, increasing by 2x (+jitter) for subsequent retries
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var RetryHandler = function () {
|
||||
this.interval = 1000 // Start at one second
|
||||
this.maxInterval = 60 * 1000 // Don't wait longer than a minute
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the function after waiting
|
||||
*
|
||||
* @param {function} fn Function to invoke
|
||||
*/
|
||||
RetryHandler.prototype.retry = function (fn) {
|
||||
setTimeout(fn, this.interval)
|
||||
this.interval = this.nextInterval_()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the counter (e.g. after successful request.)
|
||||
*/
|
||||
RetryHandler.prototype.reset = function () {
|
||||
this.interval = 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next wait time.
|
||||
* @return {number} Next wait interval, in milliseconds
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
RetryHandler.prototype.nextInterval_ = function () {
|
||||
var interval = this.interval * 2 + this.getRandomInt_(0, 1000)
|
||||
return Math.min(interval, this.maxInterval)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random int in the range of min to max. Used to add jitter to wait times.
|
||||
*
|
||||
* @param {number} min Lower bounds
|
||||
* @param {number} max Upper bounds
|
||||
* @private
|
||||
*/
|
||||
RetryHandler.prototype.getRandomInt_ = function (min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
|
||||
* files or in-memory constructs.
|
||||
*
|
||||
* @example
|
||||
* var content = new Blob(["Hello world"], {"type": "text/plain"});
|
||||
* var uploader = new MediaUploader({
|
||||
* file: content,
|
||||
* token: accessToken,
|
||||
* onComplete: function(data) { ... }
|
||||
* onError: function(data) { ... }
|
||||
* });
|
||||
* uploader.upload();
|
||||
*
|
||||
* @constructor
|
||||
* @param {object} options Hash of options
|
||||
* @param {string} options.token Access token
|
||||
* @param {blob} options.file Blob-like item to upload
|
||||
* @param {string} [options.fileId] ID of file if replacing
|
||||
* @param {object} [options.params] Additional query parameters
|
||||
* @param {string} [options.contentType] Content-type, if overriding the type of the blob.
|
||||
* @param {object} [options.metadata] File metadata
|
||||
* @param {function} [options.onComplete] Callback for when upload is complete
|
||||
* @param {function} [options.onProgress] Callback for status for the in-progress upload
|
||||
* @param {function} [options.onError] Callback if upload fails
|
||||
*/
|
||||
var MediaUploader = function (options) {
|
||||
var noop = function () {}
|
||||
this.file = options.file
|
||||
this.contentType = options.contentType || this.file.type || 'application/octet-stream'
|
||||
this.metadata = options.metadata || {
|
||||
'title': this.file.name,
|
||||
'mimeType': this.contentType
|
||||
}
|
||||
this.token = options.token
|
||||
this.onComplete = options.onComplete || noop
|
||||
this.onProgress = options.onProgress || noop
|
||||
this.onError = options.onError || noop
|
||||
this.offset = options.offset || 0
|
||||
this.chunkSize = options.chunkSize || 0
|
||||
this.retryHandler = new RetryHandler()
|
||||
|
||||
this.url = options.url
|
||||
if (!this.url) {
|
||||
var params = options.params || {}
|
||||
params.uploadType = 'resumable'
|
||||
this.url = this.buildUrl_(options.fileId, params, options.baseUrl)
|
||||
}
|
||||
this.httpMethod = options.fileId ? 'PUT' : 'POST'
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the upload.
|
||||
*/
|
||||
MediaUploader.prototype.upload = function () {
|
||||
var xhr = new XMLHttpRequest()
|
||||
|
||||
xhr.open(this.httpMethod, this.url, true)
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + this.token)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.setRequestHeader('X-Upload-Content-Length', this.file.size)
|
||||
xhr.setRequestHeader('X-Upload-Content-Type', this.contentType)
|
||||
|
||||
xhr.onload = function (e) {
|
||||
if (e.target.status < 400) {
|
||||
var location = e.target.getResponseHeader('Location')
|
||||
this.url = location
|
||||
this.sendFile_()
|
||||
} else {
|
||||
this.onUploadError_(e)
|
||||
}
|
||||
}.bind(this)
|
||||
xhr.onerror = this.onUploadError_.bind(this)
|
||||
xhr.send(JSON.stringify(this.metadata))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the actual file content.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
MediaUploader.prototype.sendFile_ = function () {
|
||||
var content = this.file
|
||||
var end = this.file.size
|
||||
|
||||
if (this.offset || this.chunkSize) {
|
||||
// Only bother to slice the file if we're either resuming or uploading in chunks
|
||||
if (this.chunkSize) {
|
||||
end = Math.min(this.offset + this.chunkSize, this.file.size)
|
||||
}
|
||||
content = content.slice(this.offset, end)
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('PUT', this.url, true)
|
||||
xhr.setRequestHeader('Content-Type', this.contentType)
|
||||
xhr.setRequestHeader('Content-Range', 'bytes ' + this.offset + '-' + (end - 1) + '/' + this.file.size)
|
||||
xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
|
||||
if (xhr.upload) {
|
||||
xhr.upload.addEventListener('progress', this.onProgress)
|
||||
}
|
||||
xhr.onload = this.onContentUploadSuccess_.bind(this)
|
||||
xhr.onerror = this.onContentUploadError_.bind(this)
|
||||
xhr.send(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for the state of the file for resumption.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
MediaUploader.prototype.resume_ = function () {
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('PUT', this.url, true)
|
||||
xhr.setRequestHeader('Content-Range', 'bytes */' + this.file.size)
|
||||
xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
|
||||
if (xhr.upload) {
|
||||
xhr.upload.addEventListener('progress', this.onProgress)
|
||||
}
|
||||
xhr.onload = this.onContentUploadSuccess_.bind(this)
|
||||
xhr.onerror = this.onContentUploadError_.bind(this)
|
||||
xhr.send()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the last saved range if available in the request.
|
||||
*
|
||||
* @param {XMLHttpRequest} xhr Request object
|
||||
*/
|
||||
MediaUploader.prototype.extractRange_ = function (xhr) {
|
||||
var range = xhr.getResponseHeader('Range')
|
||||
if (range) {
|
||||
this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle successful responses for uploads. Depending on the context,
|
||||
* may continue with uploading the next chunk of the file or, if complete,
|
||||
* invokes the caller's callback.
|
||||
*
|
||||
* @private
|
||||
* @param {object} e XHR event
|
||||
*/
|
||||
MediaUploader.prototype.onContentUploadSuccess_ = function (e) {
|
||||
if (e.target.status === 200 || e.target.status === 201) {
|
||||
this.onComplete(e.target.response)
|
||||
} else if (e.target.status === 308) {
|
||||
this.extractRange_(e.target)
|
||||
this.retryHandler.reset()
|
||||
this.sendFile_()
|
||||
} else {
|
||||
this.onContentUploadError_(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors for uploads. Either retries or aborts depending
|
||||
* on the error.
|
||||
*
|
||||
* @private
|
||||
* @param {object} e XHR event
|
||||
*/
|
||||
MediaUploader.prototype.onContentUploadError_ = function (e) {
|
||||
if (e.target.status && e.target.status < 500) {
|
||||
this.onError(e.target.response)
|
||||
} else {
|
||||
this.retryHandler.retry(this.resume_.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors for the initial request.
|
||||
*
|
||||
* @private
|
||||
* @param {object} e XHR event
|
||||
*/
|
||||
MediaUploader.prototype.onUploadError_ = function (e) {
|
||||
this.onError(e.target.response) // TODO - Retries for initial upload
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a query string from a hash/object
|
||||
*
|
||||
* @private
|
||||
* @param {object} [params] Key/value pairs for query string
|
||||
* @return {string} query string
|
||||
*/
|
||||
MediaUploader.prototype.buildQuery_ = function (params) {
|
||||
params = params || {}
|
||||
return Object.keys(params).map(function (key) {
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
|
||||
}).join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the drive upload URL
|
||||
*
|
||||
* @private
|
||||
* @param {string} [id] File ID if replacing
|
||||
* @param {object} [params] Query parameters
|
||||
* @return {string} URL
|
||||
*/
|
||||
MediaUploader.prototype.buildUrl_ = function (id, params, baseUrl) {
|
||||
var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/'
|
||||
if (id) {
|
||||
url += id
|
||||
}
|
||||
var query = this.buildQuery_(params)
|
||||
if (query) {
|
||||
url += '?' + query
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
window.MediaUploader = MediaUploader
|
|
@ -30,8 +30,6 @@ import {
|
|||
import {
|
||||
debug,
|
||||
DROPBOX_APP_KEY,
|
||||
GOOGLE_API_KEY,
|
||||
GOOGLE_CLIENT_ID,
|
||||
noteid,
|
||||
noteurl,
|
||||
urlpath,
|
||||
|
@ -908,29 +906,6 @@ if (DROPBOX_APP_KEY) {
|
|||
ui.toolbar.export.dropbox.hide()
|
||||
}
|
||||
|
||||
// check if google api key and client id are set and load scripts
|
||||
if (GOOGLE_API_KEY && GOOGLE_CLIENT_ID) {
|
||||
$('<script>')
|
||||
.attr('type', 'text/javascript')
|
||||
.attr('src', 'https://www.google.com/jsapi?callback=onGoogleAPILoaded')
|
||||
.prop('async', true)
|
||||
.prop('defer', true)
|
||||
.appendTo('body')
|
||||
} else {
|
||||
ui.toolbar.import.googleDrive.hide()
|
||||
ui.toolbar.export.googleDrive.hide()
|
||||
}
|
||||
|
||||
function onGoogleAPILoaded () {
|
||||
$('<script>')
|
||||
.attr('type', 'text/javascript')
|
||||
.attr('src', 'https://apis.google.com/js/client:plusone.js?onload=onGoogleClientLoaded')
|
||||
.prop('async', true)
|
||||
.prop('defer', true)
|
||||
.appendTo('body')
|
||||
}
|
||||
window.onGoogleAPILoaded = onGoogleAPILoaded
|
||||
|
||||
// button actions
|
||||
// share
|
||||
ui.toolbar.publish.attr('href', noteurl + '/publish')
|
||||
|
@ -979,53 +954,6 @@ ui.toolbar.export.dropbox.click(function () {
|
|||
}
|
||||
Dropbox.save(options)
|
||||
})
|
||||
function uploadToGoogleDrive (accessToken) {
|
||||
ui.spinner.show()
|
||||
var filename = renderFilename(ui.area.markdown) + '.md'
|
||||
var markdown = editor.getValue()
|
||||
var blob = new Blob([markdown], {
|
||||
type: 'text/markdown;charset=utf-8'
|
||||
})
|
||||
blob.name = filename
|
||||
var uploader = new MediaUploader({
|
||||
file: blob,
|
||||
token: accessToken,
|
||||
onComplete: function (data) {
|
||||
data = JSON.parse(data)
|
||||
showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Complete!', data.alternateLink, 'Click here to view your file', true)
|
||||
ui.spinner.hide()
|
||||
},
|
||||
onError: function (data) {
|
||||
showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Error :(', '', data, false)
|
||||
ui.spinner.hide()
|
||||
}
|
||||
})
|
||||
uploader.upload()
|
||||
}
|
||||
function googleApiAuth (immediate, callback) {
|
||||
gapi.auth.authorize(
|
||||
{
|
||||
'client_id': GOOGLE_CLIENT_ID,
|
||||
'scope': 'https://www.googleapis.com/auth/drive.file',
|
||||
'immediate': immediate
|
||||
}, callback || function () { })
|
||||
}
|
||||
function onGoogleClientLoaded () {
|
||||
googleApiAuth(true)
|
||||
buildImportFromGoogleDrive()
|
||||
}
|
||||
window.onGoogleClientLoaded = onGoogleClientLoaded
|
||||
// export to google drive
|
||||
ui.toolbar.export.googleDrive.click(function (e) {
|
||||
var token = gapi.auth.getToken()
|
||||
if (token) {
|
||||
uploadToGoogleDrive(token.access_token)
|
||||
} else {
|
||||
googleApiAuth(false, function (result) {
|
||||
uploadToGoogleDrive(result.access_token)
|
||||
})
|
||||
}
|
||||
})
|
||||
// export to gist
|
||||
ui.toolbar.export.gist.attr('href', noteurl + '/gist')
|
||||
// export to snippet
|
||||
|
@ -1075,38 +1003,6 @@ ui.toolbar.import.dropbox.click(function () {
|
|||
}
|
||||
Dropbox.choose(options)
|
||||
})
|
||||
// import from google drive
|
||||
function buildImportFromGoogleDrive () {
|
||||
/* eslint-disable no-unused-vars */
|
||||
let picker = new FilePicker({
|
||||
apiKey: GOOGLE_API_KEY,
|
||||
clientId: GOOGLE_CLIENT_ID,
|
||||
buttonEl: ui.toolbar.import.googleDrive,
|
||||
onSelect: function (file) {
|
||||
if (file.downloadUrl) {
|
||||
ui.spinner.show()
|
||||
var accessToken = gapi.auth.getToken().access_token
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
beforeSend: function (request) {
|
||||
request.setRequestHeader('Authorization', 'Bearer ' + accessToken)
|
||||
},
|
||||
url: file.downloadUrl,
|
||||
success: function (data) {
|
||||
if (file.fileExtension === 'html') { parseToEditor(data) } else { replaceAll(data) }
|
||||
},
|
||||
error: function (data) {
|
||||
showMessageModal('<i class="fa fa-cloud-download"></i> Import from Google Drive', 'Import failed :(', '', data, false)
|
||||
},
|
||||
complete: function () {
|
||||
ui.spinner.hide()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
// import from gist
|
||||
ui.toolbar.import.gist.click(function () {
|
||||
// na
|
||||
|
|
|
@ -5,6 +5,4 @@ window.version = '<%- version %>'
|
|||
|
||||
window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
|
||||
|
||||
window.GOOGLE_API_KEY = '<%- GOOGLE_API_KEY %>'
|
||||
window.GOOGLE_CLIENT_ID = '<%- GOOGLE_CLIENT_ID %>'
|
||||
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
export const GOOGLE_API_KEY = window.GOOGLE_API_KEY || ''
|
||||
export const GOOGLE_CLIENT_ID = window.GOOGLE_CLIENT_ID || ''
|
||||
export const DROPBOX_APP_KEY = window.DROPBOX_APP_KEY || ''
|
||||
|
||||
export const domain = window.domain || '' // domain name
|
||||
|
|
|
@ -22,13 +22,11 @@ export const getUIElements = () => ({
|
|||
},
|
||||
export: {
|
||||
dropbox: $('.ui-save-dropbox'),
|
||||
googleDrive: $('.ui-save-google-drive'),
|
||||
gist: $('.ui-save-gist'),
|
||||
snippet: $('.ui-save-snippet')
|
||||
},
|
||||
import: {
|
||||
dropbox: $('.ui-import-dropbox'),
|
||||
googleDrive: $('.ui-import-google-drive'),
|
||||
gist: $('.ui-import-gist'),
|
||||
snippet: $('.ui-import-snippet'),
|
||||
clipboard: $('.ui-import-clipboard')
|
||||
|
|
|
@ -32,13 +32,11 @@
|
|||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
|
||||
</li>
|
||||
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof google !== 'undefined' && google) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
|
||||
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header"><%= __('Export') %></li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-google-drive" tabindex="-1" href="#" target="_self"><i class="fa fa-cloud-upload fa-fw"></i> Google Drive</a>
|
||||
</li>
|
||||
<% if(typeof github !== 'undefined' && github) { %>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a>
|
||||
</li>
|
||||
|
@ -52,8 +50,6 @@
|
|||
<li class="dropdown-header"><%= __('Import') %></li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-google-drive" tabindex="-1" href="#" target="_self"><i class="fa fa-cloud-download fa-fw"></i> Google Drive</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-gist" href="#" data-toggle="modal" data-target="#gistImportModal"><i class="fa fa-github fa-fw"></i> Gist</a>
|
||||
</li>
|
||||
<% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %>
|
||||
|
@ -138,13 +134,11 @@
|
|||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a>
|
||||
</li>
|
||||
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof google !== 'undefined' && google) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
|
||||
<% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header"><%= __('Export') %></li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-google-drive" tabindex="-1" href="#" target="_self"><i class="fa fa-cloud-upload fa-fw"></i> Google Drive</a>
|
||||
</li>
|
||||
<% if(typeof github !== 'undefined' && github) { %>
|
||||
<li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a>
|
||||
</li>
|
||||
|
@ -158,8 +152,6 @@
|
|||
<li class="dropdown-header"><%= __('Import') %></li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-google-drive" tabindex="-1" href="#" target="_self"><i class="fa fa-cloud-download fa-fw"></i> Google Drive</a>
|
||||
</li>
|
||||
<li role="presentation"><a role="menuitem" class="ui-import-gist" href="#" data-toggle="modal" data-target="#gistImportModal"><i class="fa fa-github fa-fw"></i> Gist</a>
|
||||
</li>
|
||||
<% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %>
|
||||
|
|
|
@ -208,8 +208,6 @@ module.exports = {
|
|||
'flowchart.js',
|
||||
'js-sequence-diagrams',
|
||||
'expose?RevealMarkdown!reveal-markdown',
|
||||
path.join(__dirname, 'public/js/google-drive-upload.js'),
|
||||
path.join(__dirname, 'public/js/google-drive-picker.js'),
|
||||
path.join(__dirname, 'public/js/index.js')
|
||||
],
|
||||
'index-styles': [
|
||||
|
@ -266,8 +264,6 @@ module.exports = {
|
|||
'script!abcjs',
|
||||
'expose?io!socket.io-client',
|
||||
'expose?RevealMarkdown!reveal-markdown',
|
||||
path.join(__dirname, 'public/js/google-drive-upload.js'),
|
||||
path.join(__dirname, 'public/js/google-drive-picker.js'),
|
||||
path.join(__dirname, 'public/js/index.js')
|
||||
],
|
||||
pretty: [
|
||||
|
|
Loading…
Reference in a new issue