Better permission management

This commit is contained in:
benweet 2013-10-06 15:34:40 +01:00
parent a9b9c3eb5e
commit 4fcc27b9d3
30 changed files with 277 additions and 150 deletions

View File

@ -3,12 +3,6 @@ var VERSION = "2.1.7";
var MAIN_URL = "http://benweet.github.io/stackedit/";
var GOOGLE_ANALYTICS_ACCOUNT_ID = "UA-39556145-1";
var GOOGLE_API_KEY = "AIzaSyAeCU8CGcSkn0z9js6iocHuPBX4f_mMWkw";
var GOOGLE_SCOPES = [
"https://www.googleapis.com/auth/drive.install",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/blogger",
"https://picasaweb.google.com/data/"
];
var GOOGLE_DRIVE_APP_ID = "241271498917";
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
var DROPBOX_APP_SECRET = "851fgnucpezy84t";

View File

@ -124,6 +124,8 @@ define([
utils.setInputValue("#textarea-settings-default-content", settings.defaultContent);
// Commit message
utils.setInputValue("#input-settings-publish-commit-msg", settings.commitMsg);
// Gdrive full access
utils.setInputChecked("#input-settings-gdrive-full-access", settings.gdriveFullAccess);
// Template
utils.setInputValue("#textarea-settings-publish-template", settings.template);
// PDF template
@ -158,6 +160,8 @@ define([
newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content");
// Commit message
newSettings.commitMsg = utils.getInputTextValue("#input-settings-publish-commit-msg", event);
// Gdrive full access
newSettings.gdriveFullAccess = utils.getInputChecked("#input-settings-gdrive-full-access");
// Template
newSettings.template = utils.getInputTextValue("#textarea-settings-publish-template", event);
// PDF template
@ -576,6 +580,14 @@ define([
$("#wmd-undo-button").append($('<i class="icon-reply">')).appendTo($btnGroupElt);
$("#wmd-redo-button").append($('<i class="icon-forward">')).appendTo($btnGroupElt);
};
// Shows a dialog to force the user to click a button before opening oauth popup
var oauthRedirectCallback = undefined;
core.oauthRedirect = function(providerName, callback) {
oauthRedirectCallback = callback;
$('.oauth-redirect-provider').text(providerName);
$('.modal-oauth-redirect').modal("show");
};
// Initialize multiple things and then fire eventMgr.onReady
var isDocumentPanelShown = false;
@ -855,6 +867,7 @@ define([
trigger: 'hover',
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
});
var tooltipOpen = false;
$(".tooltip-usercustom-extension").tooltip({
html: true,
container: '.modal-settings',
@ -864,10 +877,12 @@ define([
}).click(function(e) {
$(this).tooltip('show');
$(document).on("click.tooltip-usercustom-extension", function(e) {
tooltipOpen = false;
$(".tooltip-usercustom-extension").tooltip('hide');
$(document).off("click.tooltip-usercustom-extension");
});
e.stopPropagation();
!tooltipOpen && e.stopPropagation();
tooltipOpen = true;
});
_.each(document.querySelectorAll(".tooltip-template"), function(tooltipElt) {
var $tooltipElt = $(tooltipElt);
@ -880,10 +895,12 @@ define([
}).click(function(e) {
$tooltipElt.tooltip('show');
$(document).on("click.tooltip-template", function(e) {
tooltipOpen = false;
$(".tooltip-template").tooltip('hide');
$(document).off("click.tooltip-template");
});
e.stopPropagation();
!tooltipOpen && e.stopPropagation();
tooltipOpen = true;
});
});
@ -899,6 +916,16 @@ define([
show: false
});
// OAuth redirect dialog
$('.modal-oauth-redirect').modal({
backdrop: "static",
keyboard: false,
show: false
});
$('.action-oauth-redirect').click(function() {
oauthRedirectCallback();
});
// Load images
_.each(document.querySelectorAll('img'), function(imgElt) {
var $imgElt = $(imgElt);

View File

@ -41,7 +41,9 @@ define([
var htmlCode = _.template(buttonHtmlCode.config.template, {
documentTitle: selectedFileDesc.title,
documentMarkdown: selectedFileDesc.content,
documentHTML: html
documentHTML: html,
frontMatter: selectedFileDesc.frontMatter,
publishAttributes: undefined,
});
textareaElt.value = htmlCode;
}

View File

@ -1,9 +1,9 @@
define([
"jquery",
"underscore",
"crel",
"classes/Extension",
"text!html/buttonPublish.html",
], function($, _, Extension, buttonPublishHTML) {
], function($, _, crel, Extension) {
var buttonPublish = new Extension("buttonPublish", 'Button "Publish"');
// buttonPublish.settingsBlock = '<p>Adds a "Publish document" button in the
@ -26,7 +26,6 @@ define([
button.removeClass("disabled");
}
}
;
var publisher = undefined;
buttonPublish.onPublisherCreated = function(publisherParameter) {
@ -34,13 +33,19 @@ define([
};
buttonPublish.onCreateButton = function() {
var $buttonPublishHTML = $(buttonPublishHTML);
button = $buttonPublishHTML.click(function() {
if(!$buttonPublishHTML.hasClass("disabled")) {
var button = crel('button', {
class: 'btn btn-success button-publish',
title: 'Publish this document'
}, crel('i', {
class: 'icon-share'
}));
var $button = $(button);
$button.click(function() {
if(!$button.hasClass("disabled")) {
publisher.publish();
}
});
return button[0];
return button;
};
buttonPublish.onPublishRunning = function(isRunning) {

View File

@ -55,7 +55,7 @@ define([
buttonSync.onCreateButton = function() {
var button = crel('button', {
class: 'btn btn-success',
class: 'btn btn-success button-synchronize',
title: 'Synchronize all'
}, crel('i', {
class: 'icon-refresh'

View File

@ -258,19 +258,23 @@ define([
});
});
// Set folder checkbox special behavior
// Set file checkbox behavior
_.each(documentListElt.querySelectorAll('.file .checkbox'), function(checkboxElt) {
var $checkboxElt = $(checkboxElt);
$checkboxElt.click(function(e) {
e.stopPropagation();
}).find('[type=checkbox]').change(function() {
$checkboxElt.parents('.list-group').find('.folder [type=checkbox]').prop('checked', false);
});
});
// Set folder checkbox behavior
_.each(documentListElt.querySelectorAll('.folder .checkbox'), function(checkboxElt) {
var $checkboxElt = $(checkboxElt);
$checkboxElt.click(function(e) {
e.stopPropagation();
}).find('[type=checkbox]').change(function() {
var documentCheckboxElts = $checkboxElt.parent().next().find('[type=checkbox]');
if(this.checked) {
documentCheckboxElts.prop('checked', true).prop('disabled', true);
}
else {
documentCheckboxElts.prop('checked', false).prop('disabled', false);
}
$checkboxElt.parent().next().find('[type=checkbox]').prop('checked', this.checked);
});
});
@ -327,10 +331,10 @@ define([
// Selection dropdown menu actions
$(modalElt.querySelectorAll('.action-select-all')).click(function() {
$(documentListElt.querySelectorAll('input[type="checkbox"]')).prop('checked', true).change();
$(documentListElt.querySelectorAll('input[type="checkbox"]')).prop('checked', true);
});
$(modalElt.querySelectorAll('.action-unselect-all')).click(function() {
$(documentListElt.querySelectorAll('input[type="checkbox"]')).prop('checked', false).change();
$(documentListElt.querySelectorAll('input[type="checkbox"]')).prop('checked', false);
});
// Delete selection actions

View File

@ -2,9 +2,8 @@ define([
"utils",
"classes/Extension",
"text!html/mathJaxSettingsBlock.html",
"text!libs/mathjax_config.js",
"mathjax",
], function(utils, Extension, mathJaxSettingsBlockHTML, mathjaxConfigJS) {
], function(utils, Extension, mathJaxSettingsBlockHTML) {
var mathJax = new Extension("mathJax", "MathJax", true);
mathJax.settingsBlock = mathJaxSettingsBlockHTML;

View File

@ -22,7 +22,7 @@ define([
worker.postMessage(JSON.stringify(['init', typoJS, 'en_US', aff, dic]));
var aceEditor = undefined;
var wordRegExp = XRegExp('\\p{L}+', 'g');
var wordRegExp = XRegExp('\\p{L}+(?:\'\\p{L}+)*', 'g');
var markers = [];
var timeoutId = undefined;

View File

@ -34,7 +34,7 @@ define([
tour.addSteps([
{
element: ".navbar-inner",
title: "Welcome to StackEdit",
title: "Welcome to StackEdit!",
content: "Please click <code>Next</code> to take a quick tour.",
placement: "bottom",
},
@ -60,18 +60,22 @@ define([
title: "Menu",
content: [
"<p>Use the <i class='icon-provider-stackedit'></i> menu to synchronize your document on <i class='icon-provider-gdrive'></i> <code>Google Drive</code> or <i class='icon-provider-dropbox'></i> <code>Dropbox</code>.</p>",
"Use also this menu to publish your document on <i class='icon-provider-github'></i> <code>GitHub</code>, <i class='icon-provider-blogger'></i> <code>Blogger</code>..."
"Use also this menu to publish your document on <i class='icon-provider-github'></i> <code>GitHub</code>, <i class='icon-provider-blogger'></i> <code>Blogger</code>, <i class='icon-provider-wordpress'></i> <code>WordPress</code>..."
].join(''),
placement: "right",
reflex: true,
},
{
element: "#extension-buttons button:first",
title: "Synchronize/publish",
content: [
"<p>Once imported/exported, use the <i class='icon-refresh'></i> <code>Synchronize</code> button to force the synchronization (this is done automatically every 3 minutes).</p>",
"Use also the <i class='icon-share'></i> <code>Publish</code> button to update your publications."
].join(''),
element: "#extension-buttons .button-synchronize",
title: "Synchronize",
content: "<p>Once imported/exported, use the <i class='icon-refresh'></i> <code>Synchronize</code> button to force the synchronization (this is done automatically every 3 minutes).</p>",
placement: "bottom",
reflex: true,
},
{
element: "#extension-buttons .button-publish",
title: "Update publications",
content: "Once published, use the <i class='icon-share'></i> <code>Publish</code> button to update your publications.",
placement: "bottom",
reflex: true,
},

View File

@ -3,7 +3,8 @@ define([
"underscore",
"core",
"eventMgr",
"classes/AsyncTask"
"classes/AsyncTask",
"config",
], function($, _, core, eventMgr, AsyncTask) {
var client = undefined;
@ -61,6 +62,11 @@ define([
return;
}
var immediate = true;
function oauthRedirect() {
core.oauthRedirect('Dropbox', function() {
task.chain(localAuthenticate);
});
}
function localAuthenticate() {
if(immediate === false) {
eventMgr.onMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
@ -68,7 +74,6 @@ define([
// credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
}
client.reset();
client.authenticate({
interactive: !immediate
}, function(error, client) {
@ -81,7 +86,7 @@ define([
// If immediate did not work retry without immediate flag
if(immediate === true) {
immediate = false;
task.chain(localAuthenticate);
task.chain(oauthRedirect);
return;
}
// Error

View File

@ -3,7 +3,8 @@ define([
"core",
"utils",
"eventMgr",
"classes/AsyncTask"
"classes/AsyncTask",
"config"
], function($, core, utils, eventMgr, AsyncTask) {
var connected = undefined;
@ -55,7 +56,7 @@ define([
task.chain();
return;
}
var token = localStorage["githubToken"];
var token = localStorage.githubToken;
if(token !== undefined) {
github = new Github({
token: token,
@ -64,12 +65,17 @@ define([
task.chain();
return;
}
eventMgr.onMessage("Please make sure the Github authorization popup is not blocked by your browser.");
var errorMsg = "Failed to retrieve a token from GitHub.";
// We add time for user to enter his credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
var code = undefined;
function oauthRedirect() {
core.oauthRedirect('GitHub', function() {
task.chain(getCode);
});
}
function getCode() {
eventMgr.onMessage("Please make sure the Github authorization popup is not blocked by your browser.");
localStorage.removeItem("githubCode");
authWindow = utils.popupWindow('github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID, 'stackedit-github-oauth', 960, 600);
authWindow.focus();
@ -78,7 +84,7 @@ define([
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["githubCode"];
code = localStorage.githubCode;
if(code === undefined) {
task.error(new Error(errorMsg));
return;
@ -92,7 +98,7 @@ define([
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["githubToken"] = token;
localStorage.githubToken = token;
github = new Github({
token: token,
auth: "oauth"
@ -104,7 +110,7 @@ define([
}
});
}
task.chain(getCode);
task.chain(oauthRedirect);
});
task.onError(function() {
if(intervalId !== undefined) {

View File

@ -1,13 +1,16 @@
define([
"underscore",
"jquery",
"core",
"utils",
"settings",
"eventMgr",
"classes/AsyncTask"
], function($, core, utils, eventMgr, AsyncTask) {
"classes/AsyncTask",
"config"
], function(_, $, core, utils, settings, eventMgr, AsyncTask) {
var connected = false;
var authenticated = false;
var permissionList = {};
var googleHelper = {};
@ -50,23 +53,49 @@ define([
}
// Try to authenticate with Oauth
function authenticate(task) {
function authenticate(task, permission) {
task.onRun(function() {
if(authenticated === true) {
if(_.has(permissionList, permission)) {
task.chain();
return;
}
var scopes = undefined;
if(permission == 'gdrive' && settings.gdriveFullAccess === true) {
scopes = [
"https://www.googleapis.com/auth/drive.install",
"https://www.googleapis.com/auth/drive",
];
}
else if(permission == 'gdrive' && settings.gdriveFullAccess === false) {
scopes = [
"https://www.googleapis.com/auth/drive.install",
"https://www.googleapis.com/auth/drive.file",
];
}
else if(permission == 'blogger') {
scopes = [
"https://www.googleapis.com/auth/blogger",
];
}
else if(permission == 'picasa') {
scopes = [
"https://picasaweb.google.com/data/",
];
}
var immediate = true;
function oauthRedirect() {
core.oauthRedirect('Google', function() {
task.chain(localAuthenticate);
});
}
function localAuthenticate() {
if(immediate === false) {
eventMgr.onMessage("Please make sure the Google authorization popup is not blocked by your browser.");
// If not immediate we add time for user to enter his
// credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
}
gapi.auth.authorize({
'client_id': GOOGLE_CLIENT_ID,
'scope': GOOGLE_SCOPES,
'scope': scopes,
'immediate': immediate
}, function(authResult) {
gapi.client.load('drive', 'v2', function() {
@ -75,7 +104,7 @@ define([
// flag
if(connected === true && immediate === true) {
immediate = false;
task.chain(localAuthenticate);
task.chain(oauthRedirect);
return;
}
// Error
@ -83,7 +112,7 @@ define([
return;
}
// Success
authenticated = true;
permissionList[permission] = true;
task.chain();
});
});
@ -91,11 +120,11 @@ define([
task.chain(localAuthenticate);
});
}
googleHelper.forceAuthenticate = function() {
authenticated = false;
googleHelper.forceGdriveAuthenticate = function() {
permissionList = _.omit(permissionList, 'gdrive') ;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.enqueue();
};
@ -103,7 +132,7 @@ define([
var result = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.onRun(function() {
var boundary = '-------314159265358979323846';
var delimiter = "\r\n--" + boundary + "\r\n";
@ -198,7 +227,7 @@ define([
var result = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.onRun(function() {
var body = {'title': title};
var request = gapi.client.drive.files.patch({
@ -235,7 +264,7 @@ define([
var result = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.onRun(function() {
var metadata = {
title: title,
@ -276,7 +305,7 @@ define([
var result = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'picasa');
task.onRun(function() {
var headers = {
"Slug": name
@ -331,7 +360,7 @@ define([
var newChangeId = lastChangeId || 0;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.onRun(function() {
var nextPageToken = undefined;
function retrievePageOfChanges() {
@ -383,7 +412,7 @@ define([
var task = new AsyncTask();
connect(task);
if(!skipAuth) {
authenticate(task);
authenticate(task, 'gdrive');
}
task.onRun(function() {
function recursiveDownloadMetadata() {
@ -439,7 +468,7 @@ define([
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
connect(task);
if(!skipAuth) {
authenticate(task);
authenticate(task, 'gdrive');
}
task.onRun(function() {
function recursiveDownloadContent() {
@ -512,7 +541,7 @@ define([
var doc = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'gdrive');
task.onRun(function() {
gapi.drive.realtime.load(fileId, function(result) {
// onFileLoaded
@ -552,14 +581,14 @@ define([
return;
}
else if(error.code === 401 || error.code === 403 || error.code == "token_refresh_required") {
authenticated = false;
permissionList = {};
errorMsg = "Access to Google account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.code === 0 || error.code === -1) {
connected = false;
authenticated = false;
permissionList = {};
core.setOffline();
errorMsg = "|stopPublish";
}
@ -670,7 +699,7 @@ define([
googleHelper.uploadBlogger = function(blogUrl, blogId, postId, labelList, title, content, callback) {
var task = new AsyncTask();
connect(task);
authenticate(task);
authenticate(task, 'blogger');
task.onRun(function() {
var headers = {};
var token = gapi.auth.getToken();

View File

@ -36,13 +36,12 @@ define([
task.chain();
return;
}
var serializedOauthParams = localStorage["tumblrOauthParams"];
var serializedOauthParams = localStorage.tumblrOauthParams;
if(serializedOauthParams !== undefined) {
oauthParams = JSON.parse(serializedOauthParams);
task.chain();
return;
}
eventMgr.onMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
var errorMsg = "Failed to retrieve a token from Tumblr.";
// We add time for user to enter his credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
@ -51,14 +50,20 @@ define([
$.getJSON(TUMBLR_PROXY_URL + "request_token", function(data) {
if(data.oauth_token !== undefined) {
oauth_object = data;
task.chain(getVerifier);
task.chain(oauthRedirect);
}
else {
task.error(new Error(errorMsg));
}
});
}
function oauthRedirect() {
core.oauthRedirect('Tumblr', function() {
task.chain(getVerifier);
});
}
function getVerifier() {
eventMgr.onMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
localStorage.removeItem("tumblrVerifier");
authWindow = utils.popupWindow('tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token, 'stackedit-tumblr-oauth', 800, 600);
authWindow.focus();
@ -67,7 +72,7 @@ define([
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
oauth_object.oauth_verifier = localStorage["tumblrVerifier"];
oauth_object.oauth_verifier = localStorage.tumblrVerifier;
if(oauth_object.oauth_verifier === undefined) {
task.error(new Error(errorMsg));
return;
@ -80,7 +85,7 @@ define([
function getAccessToken() {
$.getJSON(TUMBLR_PROXY_URL + "access_token", oauth_object, function(data) {
if(data.access_token !== undefined && data.access_token_secret !== undefined) {
localStorage["tumblrOauthParams"] = JSON.stringify(data);
localStorage.tumblrOauthParams = JSON.stringify(data);
oauthParams = data;
task.chain();
}

View File

@ -32,17 +32,22 @@ define([
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
token = localStorage["wordpressToken"];
token = localStorage.wordpressToken;
if(token !== undefined) {
task.chain();
return;
}
eventMgr.onMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
var errorMsg = "Failed to retrieve a token from Wordpress.";
// We add time for user to enter his credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
var code = undefined;
function oauthRedirect() {
core.oauthRedirect('WordPress', function() {
task.chain(getCode);
});
}
function getCode() {
eventMgr.onMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
localStorage.removeItem("wordpressCode");
authWindow = utils.popupWindow('wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID, 'stackedit-wordpress-oauth', 960, 600);
authWindow.focus();
@ -51,7 +56,7 @@ define([
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["wordpressCode"];
code = localStorage.wordpressCode;
if(code === undefined) {
task.error(new Error(errorMsg));
return;
@ -65,7 +70,7 @@ define([
$.getJSON(WORDPRESS_PROXY_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["wordpressToken"] = token;
localStorage.wordpressToken = token;
task.chain();
}
else {
@ -73,7 +78,7 @@ define([
}
});
}
task.chain(getCode);
task.chain(oauthRedirect);
});
task.onError(function() {
if(intervalId !== undefined) {

View File

@ -745,23 +745,6 @@
class="form-control">
</div>
</div>
<div class="form-group modal-publish-blogger">
<label class="col-lg-4 control-label" for="input-publish-labels">Labels
(comma separated)</label>
<div class="col-lg-7">
<input type="text" id="input-publish-labels"
placeholder="Label1, Label2" class="form-control">
</div>
</div>
<div
class="form-group modal-publish-tumblr modal-publish-wordpress">
<label class="col-lg-4 control-label" for="input-publish-tags">Tags
(comma separated)</label>
<div class="col-lg-7">
<input type="text" id="input-publish-tags"
placeholder="Tag1, Tag2" class="form-control">
</div>
</div>
<div class="form-group modal-publish-dropbox">
<label class="col-lg-4 control-label"
for="input-publish-dropbox-path">File path</label>
@ -833,6 +816,12 @@
</div>
</div>
</div>
<blockquote class="front-matter-info modal-publish-blogger modal-publish-tumblr modal-publish-wordpress">
<p><b>NOTE:</b> You can use a
<a href="http://jekyllrb.com/docs/frontmatter/"
target="_blank">YAML front matter</a> to specify the title and the tags/labels of your publication.</p>
<p><b>Interpreted variables:</b> <i>title, categories/tags</i></p>
</blockquote>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
@ -972,11 +961,14 @@
<div class="tab-pane" id="tabpane-settings-services">
<div class="form-horizontal">
<div class="form-group">
<label class="col-lg-4 control-label"
for="input-settings-publish-commit-msg">Commit message</label>
<label class="col-lg-4 control-label">Permission</label>
<div class="col-lg-7">
<input type="text" id="input-settings-publish-commit-msg"
class="form-control">
<div class="checkbox">
<label>
<input type="checkbox" id="input-settings-gdrive-full-access" />
Allow StackEdit to open any document in Google Drive
</label>
</div>
</div>
</div>
<div class="form-group">
@ -999,6 +991,14 @@
class="form-control"></textarea>
</div>
</div>
<div class="form-group">
<label class="col-lg-4 control-label"
for="input-settings-publish-commit-msg">GitHub commit message</label>
<div class="col-lg-7">
<input type="text" id="input-settings-publish-commit-msg"
class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-lg-4 control-label"
for="input-settings-ssh-proxy">SSH proxy</label>
@ -1082,6 +1082,24 @@
</div>
<div class="modal modal-oauth-redirect">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Authorization</h3>
</div>
<div class="modal-body">
<blockquote><b>NOTE:</b> You are being redirected to <span class="oauth-redirect-provider"></span> authorization page.</blockquote>
</div>
<div class="modal-footer">
<a class="btn btn-primary action-oauth-redirect" data-dismiss="modal">Continue</a>
</div>
</div>
</div>
</div>
<div class="modal modal-app-reset">
<div class="modal-dialog">
<div class="modal-content">

View File

@ -1,3 +0,0 @@
<button class="btn btn-success" title="Publish this document">
<i class="icon-share"></i>
</button>

View File

@ -1,26 +1,31 @@
Available variables:
<br>
<ul>
<li><b>documentTitle</b>: document title</li>
<li><b>documentMarkdown</b>: document in Markdown format</li>
<li><b>documentHTML</b>: document in HTML format</li>
<li><b>publishAttributes</b>: attributes of the publish location
(undefined if not publishing)</li>
<li>
<b>documentTitle</b>: document title</li>
<li>
<b>documentMarkdown</b>: document in Markdown format</li>
<li>
<b>documentHTML</b>: document in HTML format</li>
<li>
<b>frontMatter</b>: YAML front matter object (undefined if not present)</li>
<li>
<b>publishAttributes</b>: attributes of the publish location (undefined if
not publishing)</li>
</ul>
<b>Examples:</b>
<br />
&lt;title&gt;&lt;%= documentTitle %&gt;&lt;&#x2F;title&gt;
<br />
&lt;div&gt;&lt;%- documentHTML %&gt;&lt;&#x2F;div&gt;
<br />
&lt;%<br />
if(publishAttributes.provider.providerId == &quot;github&quot;)
print(documentMarkdown);<br />
%&gt;
<br />&lt;title&gt;&lt;%= documentTitle %&gt;&lt;&#x2F;title&gt;
<br />&lt;div&gt;&lt;%- documentHTML %&gt;&lt;&#x2F;div&gt;
<br />&lt;%
<br />if(publishAttributes.provider.providerId == &quot;github&quot;) print(documentMarkdown);
<br
/>%&gt;
<br />
<br />
<a target="_blank" href="http://underscorejs.org/#template">More
info</a>
<br />
<br />
<b class="text-danger"><i class="icon-attention"></i> Careful! Template is subject to malicious code. Don't copy/paste untrusted content.</b>
<b class="text-danger">
<i class="icon-attention"></i>Careful! Template is subject to malicious code. Don't copy/paste untrusted
content.</b>

View File

@ -1,4 +1,4 @@
require([
define([
"settings",
"text!libs/mathjax_config.js"
], function(settings, mathjaxConfigJS) {

View File

@ -11,8 +11,14 @@ define([
"blogger-url"
];
bloggerProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.uploadBlogger(publishAttributes.blogUrl, publishAttributes.blogId, publishAttributes.postId, publishAttributes.labelList, title, content, function(error, blogId, postId) {
bloggerProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var labelList = publishAttributes.labelList || [];
if(frontMatter) {
frontMatter.categories !== undefined && (labelList = frontMatter.categories);
frontMatter.tags !== undefined && (labelList = frontMatter.tags);
}
_.isString(labelList) && (labelList = _.compact(labelList.split(/[\s,]/)));
googleHelper.uploadBlogger(publishAttributes.blogUrl, publishAttributes.blogId, publishAttributes.postId, labelList, title, content, function(error, blogId, postId) {
if(error) {
callback(error);
return;
@ -30,13 +36,6 @@ define([
publishAttributes.blogUrl = utils.checkUrl(blogUrl);
}
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.labelList = [];
var labels = utils.getInputTextValue("#input-publish-labels");
if(labels !== undefined) {
publishAttributes.labelList = _.chain(labels.split(",")).map(function(label) {
return utils.trim(label);
}).compact().value();
}
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -207,7 +207,7 @@ define([
});
};
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
dropboxProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var path = checkPath(publishAttributes.path);
if(path === undefined) {
callback(true);

View File

@ -244,7 +244,7 @@ define([
});
};
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
gdriveProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var contentType = publishAttributes.format != "markdown" ? 'text/html' : undefined;
googleHelper.upload(publishAttributes.id, undefined, publishAttributes.fileName || title, content, contentType, undefined, function(error, result) {
if(error) {
@ -433,7 +433,7 @@ define([
}, function(err) {
console.error(err);
if(err.type == "token_refresh_required") {
googleHelper.forceAuthenticate();
googleHelper.forceGdriveAuthenticate();
}
else if(err.type == "not_found") {
eventMgr.onError('"' + fileDesc.title + '" has been removed from Google Drive.');

View File

@ -13,7 +13,7 @@ define([
"filename"
];
gistProvider.publish = function(publishAttributes, title, content, callback) {
gistProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
githubHelper.uploadGist(publishAttributes.gistId, publishAttributes.filename, publishAttributes.isPublic, title, content, function(error, gistId) {
if(error) {
callback(error);

View File

@ -12,7 +12,7 @@ define([
"github-branch"
];
githubProvider.publish = function(publishAttributes, title, content, callback) {
githubProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var commitMsg = settings.commitMsg;
githubHelper.upload(publishAttributes.repository, publishAttributes.username, publishAttributes.branch, publishAttributes.path, content, commitMsg, callback);
};

View File

@ -12,7 +12,7 @@ define([
"ssh-password"
];
sshProvider.publish = function(publishAttributes, title, content, callback) {
sshProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
sshHelper.upload(publishAttributes.host, publishAttributes.port, publishAttributes.username, publishAttributes.password, publishAttributes.path, title, content, callback);
};

View File

@ -1,16 +1,23 @@
define([
"underscore",
"utils",
"classes/Provider",
"helpers/tumblrHelper"
], function(utils, Provider, tumblrHelper) {
], function(_, utils, Provider, tumblrHelper) {
var tumblrProvider = new Provider("tumblr", "Tumblr");
tumblrProvider.publishPreferencesInputIds = [
"tumblr-hostname"
];
tumblrProvider.publish = function(publishAttributes, title, content, callback) {
tumblrHelper.upload(publishAttributes.blogHostname, publishAttributes.postId, publishAttributes.tags, publishAttributes.format == "markdown" ? "markdown" : "html", title, content, function(error, postId) {
tumblrProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var labelList = publishAttributes.tags || [];
if(frontMatter) {
frontMatter.categories !== undefined && (labelList = frontMatter.categories);
frontMatter.tags !== undefined && (labelList = frontMatter.tags);
}
_.isString(labelList) && (labelList = _.compact(labelList.split(/[\s,]/)));
tumblrHelper.upload(publishAttributes.blogHostname, publishAttributes.postId, labelList.join(','), publishAttributes.format == "markdown" ? "markdown" : "html", title, content, function(error, postId) {
if(error) {
callback(error);
return;
@ -24,7 +31,6 @@ define([
var publishAttributes = {};
publishAttributes.blogHostname = utils.getInputTextValue("#input-publish-tumblr-hostname", event, /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -1,8 +1,9 @@
define([
"underscore",
"utils",
"classes/Provider",
"helpers/wordpressHelper"
], function(utils, Provider, wordpressHelper) {
], function(_, utils, Provider, wordpressHelper) {
var wordpressProvider = new Provider("wordpress", "WordPress");
wordpressProvider.defaultPublishFormat = "html";
@ -10,8 +11,14 @@ define([
"wordpress-site"
];
wordpressProvider.publish = function(publishAttributes, title, content, callback) {
wordpressHelper.upload(publishAttributes.site, publishAttributes.postId, publishAttributes.tags, title, content, function(error, postId) {
wordpressProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
var labelList = publishAttributes.tags || [];
if(frontMatter) {
frontMatter.categories !== undefined && (labelList = frontMatter.categories);
frontMatter.tags !== undefined && (labelList = frontMatter.tags);
}
_.isString(labelList) && (labelList = _.compact(labelList.split(/[\s,]/)));
wordpressHelper.upload(publishAttributes.site, publishAttributes.postId, labelList.join(','), title, content, function(error, postId) {
if(error) {
callback(error);
return;
@ -25,7 +32,6 @@ define([
var publishAttributes = {};
publishAttributes.site = utils.getInputTextValue("#input-publish-wordpress-site", event, /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -63,6 +63,7 @@ define([
documentTitle: fileDesc.title,
documentMarkdown: fileDesc.content,
documentHTML: html,
frontMatter: fileDesc.frontMatter,
publishAttributes: publishAttributes
});
}
@ -108,9 +109,10 @@ define([
// Format the content
var content = getPublishContent(publishFileDesc, publishAttributes, publishHTML);
var title = (publishFileDesc.frontMatter || {}).title || publishFileDesc.title;
// Call the provider
publishAttributes.provider.publish(publishAttributes, publishFileDesc.title, content, function(error) {
publishAttributes.provider.publish(publishAttributes, publishFileDesc.frontMatter, title, content, function(error) {
if(error !== undefined) {
var errorMsg = error.toString();
if(errorMsg.indexOf("|removePublish") !== -1) {
@ -178,7 +180,7 @@ define([
$(".publish-provider-name").text(provider.providerName);
// Show/hide controls depending on provider
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
$('.modal-publish [class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
// Reset fields
utils.resetModalInputs();
@ -219,7 +221,8 @@ define([
var fileDesc = fileMgr.currentFile;
var html = previewHtml;
var content = getPublishContent(fileDesc, publishAttributes, html);
provider.publish(publishAttributes, fileDesc.title, content, function(error) {
var title = (fileDesc.frontMatter && fileDesc.frontMatter.title) || fileDesc.title;
provider.publish(publishAttributes, fileDesc.frontMatter, title, content, function(error) {
if(error === undefined) {
publishAttributes.provider = provider;
sharing.createLink(publishAttributes, function() {

View File

@ -12,6 +12,7 @@ define([
maxWidth: 960,
defaultContent: "\n\n\n> Written with [StackEdit](" + MAIN_URL + ").",
commitMsg: "Published with " + MAIN_URL,
gdriveFullAccess: true,
template: [
'<!DOCTYPE html>\n',
'<html>\n',

View File

@ -13,6 +13,7 @@
@primary-bg: #ddd;
@primary-bg-light: lighten(@primary-bg, 4%);
@primary-bg-lighter: lighten(@primary-bg, 8%);
@primary-bg-lightest: lighten(@primary-bg, 9.5%);
@secondary-bg: #888;
@secondary-bg-light: lighten(@secondary-bg, 7%);
@secondary-bg-dark: darken(@secondary-bg, 7%);
@ -44,7 +45,7 @@
@dropdown-border: @primary-bg;
@pre-border-color: @primary-bg;
@navbar-default-bg: @primary-bg;
@nav-link-hover-bg: @primary-bg-lighter;
@nav-link-hover-bg: @primary-bg-lightest;
@nav-disabled-link-color: @disabled-color;
@nav-disabled-link-hover-color: @disabled-color;
@nav-tabs-border-color: @transparent;
@ -55,7 +56,7 @@
@list-group-link-color: @primary-color;
@list-group-border: @transparent;
@list-group-active-bg: @dropdown-link-hover-bg;
@list-group-hover-bg: @primary-bg-lighter;
@list-group-hover-bg: @primary-bg-lightest;
@input-color-placeholder: @disabled-color;
@btn-default-color: @primary-color;
@btn-default-bg: @transparent;
@ -396,6 +397,11 @@ body {
background-color: @panel-bg;
padding-top: 6px;
border-right: 5px solid @panel-border-color;
& > .nav > li > a {
&:hover, &:active {
background-color: @panel-bg;
}
}
}
}
@ -980,7 +986,7 @@ ul,ol {
}
.ace_marker-layer .ace_active-line {
background-color: @primary-bg-lighter;
background-color: @primary-bg-lightest;
}
.ace_print-margin {

View File

@ -3,6 +3,7 @@
@primary-bg: #444;
@primary-bg-light: darken(@primary-bg, 7%);
@primary-bg-lighter: darken(@primary-bg, 13%);
@primary-bg-lightest: darken(@primary-bg, 15%);
@secondary-bg: #444;
@secondary-bg-light: darken(@secondary-bg, 7%);
@secondary-bg-dark: lighten(@secondary-bg, 7%);