summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.sequelizerc8
-rw-r--r--README.md8
-rw-r--r--app.js44
-rw-r--r--config.json5
-rw-r--r--lib/auth.js28
-rw-r--r--lib/config.js6
-rw-r--r--lib/migrations/20160515114000-user-add-tokens.js15
-rw-r--r--lib/models/user.js11
-rw-r--r--lib/response.js16
-rw-r--r--package.json2
-rw-r--r--public/css/index.css5
-rw-r--r--public/js/common.js6
-rw-r--r--public/js/index.js227
-rw-r--r--public/views/body.ejs83
-rw-r--r--public/views/foot.ejs5
-rw-r--r--public/views/header.ejs20
-rw-r--r--public/views/index.ejs38
-rw-r--r--public/views/signin-modal.ejs5
18 files changed, 490 insertions, 42 deletions
diff --git a/.sequelizerc b/.sequelizerc
new file mode 100644
index 00000000..29a0d145
--- /dev/null
+++ b/.sequelizerc
@@ -0,0 +1,8 @@
+var path = require('path');
+
+module.exports = {
+ 'config': path.resolve('config.json'),
+ 'migrations-path': path.resolve('lib', 'migrations'),
+ 'models-path': path.resolve('lib', 'models'),
+ 'url': 'change this'
+} \ No newline at end of file
diff --git a/README.md b/README.md
index 9f5fbb1c..f2b469e2 100644
--- a/README.md
+++ b/README.md
@@ -123,8 +123,12 @@ Third-party integration api key settings
| ------- | --------- | ----------- |
| facebook, twitter, github, dropbox | `config.json` | for signin |
| imgur | `config.json` | for image upload |
-| dropbox | `public/views/foot.ejs` | for chooser and saver |
-| google drive | `public/js/common.js` | for export and import |
+| google drive, dropbox | `public/js/common.js` | for export and import |
+
+DB migration
+---
+Modify the file named `.sequelizerc`, change the value of the variable `url` with your db connection string.
+And run `node_modules/.bin/sequelize db:migrate`, that's it!
Operational Transformation
---
diff --git a/app.js b/app.js
index bfb9ac7d..35408bb0 100644
--- a/app.js
+++ b/app.js
@@ -16,6 +16,7 @@ var formidable = require('formidable');
var morgan = require('morgan');
var passportSocketIo = require("passport.socketio");
var helmet = require('helmet');
+var request = require('request');
//core
var config = require("./lib/config.js");
@@ -82,6 +83,9 @@ var sessionStore = new SequelizeStore({
//compression
app.use(compression());
+//cookies
+app.use(cookieParser());
+
// use hsts to tell https users stick to this
app.use(helmet.hsts({
maxAge: 31536000 * 1000, // 365 days
@@ -292,6 +296,23 @@ if (config.github) {
//github callback actions
app.get('/auth/github/callback/:noteId/:action', response.githubActions);
}
+//gitlab auth
+if (config.gitlab) {
+ app.get('/auth/gitlab',
+ passport.authenticate('gitlab'),
+ function (req, res) {});
+ //gitlab auth callback
+ app.get('/auth/gitlab/callback',
+ passport.authenticate('gitlab', {
+ failureRedirect: config.serverurl
+ }),
+ function (req, res) {
+ res.redirect(config.serverurl);
+ });
+ //gitlab callback actions
+ // TODO: Maybe in the future
+ //app.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions);
+}
//dropbox auth
if (config.dropbox) {
app.get('/auth/dropbox',
@@ -421,6 +442,29 @@ app.post('/uploadimage', function (req, res) {
}
});
});
+//get gitlab parameters
+app.get('/gitlab', function (req, res) {
+ var ret = { baseURL: config.gitlab.baseURL };
+ models.User.findById(req.cookies.userid)
+ .then(function(user) {
+ ret.accesstoken = user.accessToken;
+ ret.profileid = user.profileid;
+ request(
+ config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
+ function(error, httpResponse, body) {
+ if (!error && httpResponse.statusCode == 200) {
+ ret.projects = JSON.parse(body);
+ return res.send(ret);
+ } else {
+ return res.send(ret);
+ }
+ }
+ );
+ }).catch(function(err) {
+ logger.error('user search failed: ' + err);
+ return response.errorInternalError(res);
+ });
+});
//get new note
app.get("/new", response.newNote);
//get publish note
diff --git a/config.json b/config.json
index 16ad258b..c65e9c7c 100644
--- a/config.json
+++ b/config.json
@@ -32,6 +32,11 @@
"clientID": "change this",
"clientSecret": "change this"
},
+ "gitlab": {
+ "baseURL": "change this",
+ "clientID": "change this",
+ "clientSecret": "change this"
+ },
"dropbox": {
"clientID": "change this",
"clientSecret": "change this"
diff --git a/lib/auth.js b/lib/auth.js
index af3e8d1d..ec45eea3 100644
--- a/lib/auth.js
+++ b/lib/auth.js
@@ -4,6 +4,7 @@ var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
var TwitterStrategy = require('passport-twitter').Strategy;
var GithubStrategy = require('passport-github').Strategy;
+var GitlabStrategy = require('passport-gitlab2').Strategy;
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
//core
@@ -18,13 +19,23 @@ function callback(accessToken, refreshToken, profile, done) {
profileid: profile.id.toString()
},
defaults: {
- profile: JSON.stringify(profile)
+ profile: JSON.stringify(profile),
+ accessToken: accessToken,
+ refreshToken: refreshToken
}
}).spread(function(user, created) {
if (user) {
- if (config.debug)
- logger.info('user login: ' + user.id);
- return done(null, user);
+ if(user.accessToken == accessToken){
+ if (config.debug)
+ logger.info('user login: ' + user.id);
+ return done(null, user);
+ }
+ user.accessToken = accessToken;
+ user.save().then(function(){
+ if (config.debug)
+ logger.info('user login: ' + user.id);
+ return done(null, user);
+ })
}
}).catch(function(err) {
logger.error('auth callback failed: ' + err);
@@ -56,6 +67,15 @@ if (config.github) {
callbackURL: config.serverurl + '/auth/github/callback'
}, callback));
}
+//gitlab
+if (config.gitlab) {
+ passport.use(new GitlabStrategy({
+ baseURL: config.gitlab.baseURL,
+ clientID: config.gitlab.clientID,
+ clientSecret: config.gitlab.clientSecret,
+ callbackURL: config.serverurl + '/auth/gitlab/callback'
+ }, callback));
+}
//dropbox
if (config.dropbox) {
passport.use(new DropboxStrategy({
diff --git a/lib/config.js b/lib/config.js
index 6738d4a8..cdaec31c 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -25,7 +25,7 @@ var db = config.db || {
};
// ssl path
-var sslkeypath = config.sslkeypath || ''
+var sslkeypath = config.sslkeypath || '';
var sslcertpath = config.sslcertpath || '';
var sslcapath = config.sslcapath || '';
var dhparampath = config.dhparampath || '';
@@ -59,6 +59,7 @@ var documentmaxlength = config.documentmaxlength || 100000;
var facebook = config.facebook || false;
var twitter = config.twitter || false;
var github = config.github || false;
+var gitlab = config.gitlab || false;
var dropbox = config.dropbox || false;
var imgur = config.imgur || false;
@@ -110,6 +111,7 @@ module.exports = {
facebook: facebook,
twitter: twitter,
github: github,
+ gitlab: gitlab,
dropbox: dropbox,
imgur: imgur
-}; \ No newline at end of file
+};
diff --git a/lib/migrations/20160515114000-user-add-tokens.js b/lib/migrations/20160515114000-user-add-tokens.js
new file mode 100644
index 00000000..3af490a9
--- /dev/null
+++ b/lib/migrations/20160515114000-user-add-tokens.js
@@ -0,0 +1,15 @@
+"use strict";
+
+module.exports = {
+ up: function (queryInterface, Sequelize) {
+ queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING);
+ queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING);
+ return;
+ },
+
+ down: function (queryInterface, Sequelize) {
+ queryInterface.removeColumn('Users', 'accessToken');
+ queryInterface.removeColumn('Users', 'refreshToken');
+ return;
+ }
+}; \ No newline at end of file
diff --git a/lib/models/user.js b/lib/models/user.js
index e1a373d6..64173cbd 100644
--- a/lib/models/user.js
+++ b/lib/models/user.js
@@ -23,6 +23,12 @@ module.exports = function (sequelize, DataTypes) {
},
history: {
type: DataTypes.TEXT
+ },
+ accessToken: {
+ type: DataTypes.STRING
+ },
+ refreshToken: {
+ type: DataTypes.STRING
}
}, {
classMethods: {
@@ -63,6 +69,9 @@ module.exports = function (sequelize, DataTypes) {
case "github":
photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48';
break;
+ case "gitlab":
+ photo = profile.avatarUrl;
+ break;
case "dropbox":
//no image api provided, use gravatar
photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
@@ -72,6 +81,6 @@ module.exports = function (sequelize, DataTypes) {
}
}
});
-
+
return User;
}; \ No newline at end of file
diff --git a/lib/response.js b/lib/response.js
index 7a75e234..99cd080a 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -48,7 +48,7 @@ var response = {
showNote: showNote,
showPublishNote: showPublishNote,
showPublishSlide: showPublishSlide,
- showIndex: showIndex,
+ showIndex: showIndex,
noteActions: noteActions,
publishNoteActions: publishNoteActions,
githubActions: githubActions
@@ -72,7 +72,7 @@ function responseError(res, code, detail, msg) {
code: code,
detail: detail,
msg: msg,
- useCDN: config.usecdn
+ useCDN: config.usecdn
});
res.write(content);
res.end();
@@ -94,7 +94,8 @@ function showIndex(req, res, next) {
facebook: config.facebook,
twitter: config.twitter,
github: config.github,
- dropbox: config.dropbox,
+ gitlab: config.gitlab,
+ dropbox: config.dropbox
});
res.write(content);
res.end();
@@ -124,7 +125,8 @@ function responseHackMD(res, note) {
facebook: config.facebook,
twitter: config.twitter,
github: config.github,
- dropbox: config.dropbox,
+ gitlab: config.gitlab,
+ dropbox: config.dropbox
});
var buf = html;
res.writeHead(200, {
@@ -355,7 +357,7 @@ function publishNoteActions(req, res, next) {
res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
break;
default:
- res.redirect(config.serverurl + '/s/' + note.shortid);
+ res.redirect(config.serverurl + '/s/' + note.shortid);
break;
}
});
@@ -370,7 +372,7 @@ function githubActions(req, res, next) {
githubActionGist(req, res, note);
break;
default:
- res.redirect(config.serverurl + '/' + noteId);
+ res.redirect(config.serverurl + '/' + noteId);
break;
}
});
@@ -470,4 +472,4 @@ var render = function (res, title, markdown) {
}));
};
-module.exports = response; \ No newline at end of file
+module.exports = response;
diff --git a/package.json b/package.json
index a70bce31..bacd2336 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"passport-dropbox-oauth2": "^1.0.0",
"passport-facebook": "^2.1.0",
"passport-github": "^1.1.0",
+ "passport-gitlab2": "^2.2.0",
"passport-twitter": "^1.0.4",
"passport.socketio": "^3.6.1",
"pg": "^4.5.3",
@@ -47,6 +48,7 @@
"request": "^2.72.0",
"reveal.js": "3.2.0",
"sequelize": "^3.21.0",
+ "sequelize-cli": "^2.4.0",
"shortid": "2.2.6",
"socket.io": "1.4.5",
"sqlite3": "^3.1.3",
diff --git a/public/css/index.css b/public/css/index.css
index fdfae0ac..aaf84a04 100644
--- a/public/css/index.css
+++ b/public/css/index.css
@@ -328,6 +328,11 @@ div[contenteditable]:empty:not(:focus):before{
border-bottom: 1px solid #ccc;
}
+.snippet-import-or {
+ text-align: center;
+ width: 100%;
+}
+
.status-bar {
background: #1c1c1e;
border-top: 1px solid #343434;
diff --git a/public/js/common.js b/public/js/common.js
index c623cd24..33b30689 100644
--- a/public/js/common.js
+++ b/public/js/common.js
@@ -4,8 +4,10 @@ var urlpath = ''; // sub url path, like: www.example.com/<urlpath>
//settings
var debug = false;
-var GOOGLE_API_KEY = 'change this';
-var GOOGLE_CLIENT_ID = 'change this';
+var GOOGLE_API_KEY = '';
+var GOOGLE_CLIENT_ID = '';
+
+var DROPBOX_APP_KEY = '';
var port = window.location.port;
var serverurl = window.location.protocol + '//' + (domain ? domain : window.location.hostname) + (port ? ':' + port : '') + (urlpath ? '/' + urlpath : '');
diff --git a/public/js/index.js b/public/js/index.js
index 3f0ed593..ea9194e6 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -243,6 +243,16 @@ var lastInfo = {
};
var personalInfo = {};
var onlineUsers = [];
+var fileTypes = {
+ "pl": "perl",
+ "cgi": "perl",
+ "js": "javascript",
+ "php": "php",
+ "sh": "bash",
+ "rb": "ruby",
+ "html": "html",
+ "py": "python"
+};
//editor settings
var textit = document.getElementById("textit");
@@ -496,12 +506,14 @@ var ui = {
export: {
dropbox: $(".ui-save-dropbox"),
googleDrive: $(".ui-save-google-drive"),
- gist: $(".ui-save-gist")
+ 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")
},
beta: {
@@ -541,6 +553,10 @@ var ui = {
codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"),
codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"),
markdown: $(".ui-view-area .markdown-body")
+ },
+ modal: {
+ snippetImportProjects: $("#snippetImportModalProjects"),
+ snippetImportSnippets: $("#snippetImportModalSnippets")
}
};
@@ -1048,6 +1064,35 @@ function showMessageModal(title, header, href, text, success) {
modal.modal('show');
}
+// check if dropbox app key is set and load scripts
+if (DROPBOX_APP_KEY) {
+ $('<script>')
+ .attr('type', 'text/javascript')
+ .attr('src', 'https://www.dropbox.com/static/api/2/dropins.js')
+ .attr('id', 'dropboxjs')
+ .attr('data-app-key', DROPBOX_APP_KEY)
+ .appendTo('body');
+} else {
+ ui.toolbar.import.dropbox.hide();
+ 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')
+ .appendTo('body');
+
+ $('<script>')
+ .attr('type', 'text/javascript')
+ .attr('src', 'https://apis.google.com/js/client:plusone.js?onload=onGoogleClientLoaded')
+ .appendTo('body');
+} else {
+ ui.toolbar.import.googleDrive.hide();
+ ui.toolbar.export.googleDrive.hide();
+}
+
//button actions
//share
ui.toolbar.publish.attr("href", noteurl + "/publish");
@@ -1134,6 +1179,41 @@ ui.toolbar.export.googleDrive.click(function (e) {
});
//export to gist
ui.toolbar.export.gist.attr("href", noteurl + "/gist");
+//export to snippet
+ui.toolbar.export.snippet.click(function() {
+ ui.spinner.show();
+ $.get(serverurl + '/gitlab')
+ .success(function (data) {
+ $("#snippetExportModalAccessToken").val(data.accesstoken);
+ $("#snippetExportModalBaseURL").val(data.baseURL);
+ $("#snippetExportModalLoading").hide();
+ $("#snippetExportModal").modal('toggle');
+ $("#snippetExportModalProjects").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>');
+ if (data.projects) {
+ data.projects.sort(function(a,b) {
+ return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0);
+ });
+ data.projects.forEach(function(project) {
+ if (!project.snippets_enabled
+ || (project.permissions.project_access === null && project.permissions.group_access === null)
+ || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20))
+ {
+ return;
+ }
+ $('<option>').val(project.id).text(project.path_with_namespace).appendTo("#snippetExportModalProjects");
+ });
+ $("#snippetExportModalProjects").prop('disabled',false);
+ }
+ $("#snippetExportModalLoading").hide();
+ })
+ .error(function (data) {
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false);
+ })
+ .complete(function () {
+ ui.spinner.hide();
+ });
+ return false;
+});
//import from dropbox
ui.toolbar.import.dropbox.click(function () {
var options = {
@@ -1186,6 +1266,43 @@ function buildImportFromGoogleDrive() {
ui.toolbar.import.gist.click(function () {
//na
});
+//import from snippet
+ui.toolbar.import.snippet.click(function () {
+ ui.spinner.show();
+ $.get(serverurl + '/gitlab')
+ .success(function (data) {
+ $("#snippetImportModalAccessToken").val(data.accesstoken);
+ $("#snippetImportModalBaseURL").val(data.baseURL);
+ $("#snippetImportModalContent").prop('disabled', false);
+ $("#snippetImportModalConfirm").prop('disabled', false);
+ $("#snippetImportModalLoading").hide();
+ $("#snippetImportModal").modal('toggle');
+ $("#snippetImportModalProjects").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>');
+ if (data.projects) {
+ data.projects.sort(function(a,b) {
+ return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0);
+ });
+ data.projects.forEach(function(project) {
+ if (!project.snippets_enabled
+ || (project.permissions.project_access === null && project.permissions.group_access === null)
+ || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20))
+ {
+ return;
+ }
+ $('<option>').val(project.id).text(project.path_with_namespace).appendTo("#snippetImportModalProjects");
+ });
+ $("#snippetImportModalProjects").prop('disabled',false);
+ }
+ $("#snippetImportModalLoading").hide();
+ })
+ .error(function (data) {
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false);
+ })
+ .complete(function () {
+ //na
+ });
+ return false;
+});
//import from clipboard
ui.toolbar.import.clipboard.click(function () {
//na
@@ -1207,6 +1324,39 @@ ui.toolbar.beta.pdf.attr("download", "").attr("href", noteurl + "/pdf");
//slide
ui.toolbar.beta.slide.attr("href", noteurl + "/slide");
+//modal actions
+//snippet projects
+ui.modal.snippetImportProjects.change(function() {
+ var accesstoken = $("#snippetImportModalAccessToken").val(),
+ baseURL = $("#snippetImportModalBaseURL").val(),
+ project = $("#snippetImportModalProjects").val();
+
+ $("#snippetImportModalLoading").show();
+ $("#snippetImportModalContent").val('/projects/' + project);
+ $.get(baseURL + '/api/v3/projects/' + project + '/snippets?access_token=' + accesstoken)
+ .success(function(data) {
+ $("#snippetImportModalSnippets").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>');
+ data.forEach(function(snippet) {
+ $('<option>').val(snippet.id).text(snippet.title).appendTo($("#snippetImportModalSnippets"));
+ });
+ $("#snippetImportModalLoading").hide();
+ $("#snippetImportModalSnippets").prop('disabled',false);
+ })
+ .error(function(err) {
+
+ })
+ .complete(function() {
+ //na
+ });
+});
+//snippet snippets
+ui.modal.snippetImportSnippets.change(function() {
+ var project = $("#snippetImportModalProjects").val(),
+ snippet = $("#snippetImportModalSnippets").val();
+
+ $("#snippetImportModalContent").val($("#snippetImportModalContent").val() + '/snippets/' + snippet);
+})
+
function scrollToTop() {
if (currentMode == modeType.both) {
if (editor.getScrollInfo().top != 0)
@@ -1353,6 +1503,81 @@ $("#gistImportModalConfirm").click(function () {
}
});
+// snippet import modal
+$("#snippetImportModalClear").click(function () {
+ $("#snippetImportModalContent").val('');
+ $("#snippetImportModalProjects").val('init');
+ $("#snippetImportModalSnippets").val('init');
+ $("#snippetImportModalSnippets").prop('disabled',true);
+});
+$("#snippetImportModalConfirm").click(function () {
+ var snippeturl = $("#snippetImportModalContent").val();
+ if (!snippeturl) return;
+ $('#snippetImportModal').modal('hide');
+ $("#snippetImportModalContent").val('');
+ if (!/^.+\/snippets\/.+$/.test(snippeturl)) {
+ showMessageModal('<i class="fa fa-github"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', '', false);
+ } else {
+ ui.spinner.show();
+ var accessToken = '?access_token=' + $("#snippetImportModalAccessToken").val();
+ var fullURL = $("#snippetImportModalBaseURL").val() + '/api/v3' + snippeturl;
+ $.get(fullURL + accessToken)
+ .success(function(data) {
+ var content = '# ' + (data.title || "Snippet Import");
+ var fileInfo = data.file_name.split('.');
+ fileInfo[1] = (fileInfo[1]) ? fileInfo[1] : "md";
+ $.get(fullURL + '/raw' + accessToken)
+ .success(function (raw) {
+ if (raw) {
+ content += "\n\n";
+ if (fileInfo[1] != "md") {
+ content += "```" + fileTypes[fileInfo[1]] + "\n";
+ }
+ content += raw;
+ if (fileInfo[1] != "md") {
+ content += "\n```";
+ }
+ replaceAll(content);
+ }
+ })
+ .error(function (data) {
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false);
+ })
+ .complete(function () {
+ ui.spinner.hide();
+ });
+ })
+ .error(function (data) {
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false);
+ });
+ }
+});
+
+//snippet export modal
+$("#snippetExportModalConfirm").click(function() {
+ var accesstoken = $("#snippetExportModalAccessToken").val(),
+ baseURL = $("#snippetExportModalBaseURL").val(),
+ data = {
+ title: $("#snippetExportModalTitle").val(),
+ file_name: $("#snippetExportModalFileName").val(),
+ code: editor.getValue(),
+ visibility_level: $("#snippetExportModalVisibility").val()
+ };
+
+ $("#snippetExportModalLoading").show();
+ var fullURL = baseURL + '/api/v3/projects/' + $("#snippetExportModalProjects").val() + '/snippets?access_token=' + accesstoken;
+ $.post(fullURL
+ , data
+ , function(ret) {
+ $("#snippetExportModalLoading").hide();
+ $('#snippetExportModal').modal('hide');
+ var redirect = baseURL + '/' + $("#snippetExportModalProjects option[value='" + $("#snippetExportModalProjects").val() + "']").text() + '/snippets/' + ret.id;
+ showMessageModal('<i class="fa fa-gitlab"></i> Export to Snippet', 'Export Successful!', redirect, 'View Snippet Here', true);
+ }
+ , 'json'
+ );
+});
+
function parseToEditor(data) {
var parsed = toMarkdown(data);
if (parsed)
diff --git a/public/views/body.ejs b/public/views/body.ejs
index 11bccfef..5e183dc3 100644
--- a/public/views/body.ejs
+++ b/public/views/body.ejs
@@ -151,5 +151,88 @@
</div>
</div>
</div>
+<!-- snippet import modal -->
+<div class="modal fade" id="snippetImportModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
+ </button>
+ <h4 class="modal-title" id="myModalLabel">Import from Snippet</h4>
+ </div>
+ <div class="modal-body">
+ <input type="hidden" id="snippetImportModalAccessToken" />
+ <input type="hidden" id="snippetImportModalBaseURL" />
+ <div class="ui-field-contain" style="display:table;margin-bottom:10px;width:100%;">
+ <div style="display:table-row;margin-bottom:5px;">
+ <label style="display:table-cell;">Project:</label>
+ <select class="form-control" id="snippetImportModalProjects" style="display:table-cell;" disabled="disabled">
+ <option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>
+ </select>
+ </div>
+ <div style="display:table-row;">
+ <label style="display:table-cell;">Snippet</label>
+ <select class="form-control" id="snippetImportModalSnippets" style="display:table-cell;" disabled="disabled">
+ <option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>
+ </select>
+ </div>
+ </div>
+ <p class="snippet-import-or">OR</p>
+ <input type="url" class="form-control" placeholder="/projects/:id/snippets/:snippet_id" id="snippetImportModalContent" disabled="disabled">
+ </div>
+ <div class="modal-footer">
+ <span id="snippetImportModalLoading"><i class="fa fa-refresh fa-spin"></i></span>
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="button" class="btn btn-danger" id="snippetImportModalClear">Clear</button>
+ <button type="button" class="btn btn-primary" id="snippetImportModalConfirm" disabled="disabled">Import</button>
+ </div>
+ </div>
+ </div>
+</div>
+<!-- snippet export modal -->
+<div class="modal fade" id="snippetExportModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
+ </button>
+ <h4 class="modal-title" id="myModalLabel">Export to Snippet</h4>
+ </div>
+ <div class="modal-body">
+ <input type="hidden" id="snippetExportModalAccessToken" />
+ <input type="hidden" id="snippetExportModalBaseURL" />
+ <div class="ui-field-contain" style="display:table;margin-bottom:10px;width:100%;">
+ <div style="display:table-row;margin-bottom:5px;">
+ <label style="display:table-cell;">Title:</label>
+ <input class="form-control" placeholder="new snippet" type="text" id="snippetExportModalTitle" />
+ </div>
+ <div style="display:table-row;margin-bottom:5px;">
+ <label style="display:table-cell;">File Name:</label>
+ <input class="form-control" placeholder="new_snippet.md" type="text" id="snippetExportModalFileName" />
+ </div>
+ <div style="display:table-row;margin-bottom:5px;">
+ <label style="display:table-cell;">Project:</label>
+ <select class="form-control" id="snippetExportModalProjects" style="display:table-cell;">
+ <option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>
+ </select>
+ </div>
+ <div style="display:table-row;margin-bottom:5px;">
+ <label style="display:table-cell;">Visibility:</label>
+ <select class="form-control" id="snippetExportModalVisibility" style="display:table-cell;">
+ <option value="" selected="selected" disabled="disabled">Select Visibility Level</option>
+ <option value="0">Private</option>
+ <option value="10">Internal</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <span id="snippetExportModalLoading"><i class="fa fa-refresh fa-spin"></i></span>
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="button" class="btn btn-primary" id="snippetExportModalConfirm">Export</button>
+ </div>
+ </div>
+ </div>
+</div>
<%- include signin-modal %>
<%- include help-modal %> \ No newline at end of file
diff --git a/public/views/foot.ejs b/public/views/foot.ejs
index 9cd4ba03..7adb8c72 100644
--- a/public/views/foot.ejs
+++ b/public/views/foot.ejs
@@ -71,8 +71,6 @@
<script src="<%- url %>/vendor/md-toc.js" defer></script>
<script src="<%- url %>/vendor/showup/showup.js" defer></script>
<script src="<%- url %>/vendor/randomColor.js" defer></script>
-<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="change this" async defer></script>
-<script src="https://www.google.com/jsapi" defer></script>
<script src="<%- url %>/js/google-drive-upload.js" defer></script>
<script src="<%- url %>/js/google-drive-picker.js" defer></script>
<script src="<%- url %>/js/common.js" defer></script>
@@ -80,5 +78,4 @@
<script src="<%- url %>/js/render.js" defer></script>
<script src="<%- url %>/js/history.js" defer></script>
<script src="<%- url %>/js/index.js" defer></script>
-<script src="<%- url %>/js/syncscroll.js" defer></script>
-<script src="https://apis.google.com/js/client:plusone.js?onload=onGoogleClientLoaded" defer></script> \ No newline at end of file
+<script src="<%- url %>/js/syncscroll.js" defer></script> \ No newline at end of file
diff --git a/public/views/header.ejs b/public/views/header.ejs
index bbabe778..8610b861 100644
--- a/public/views/header.ejs
+++ b/public/views/header.ejs
@@ -1,7 +1,7 @@
<nav class="navbar navbar-default navbar-fixed-top unselectable hidden-print">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
- <span class="pull-right" style="margin-top: 17px; color: #777;">
+ <span class="pull-right" style="margin-top: 17px; color: #777;">
<div class="visible-xs">&nbsp;</div>
<div class="visible-sm">&nbsp;</div>
<div class="visible-md">&nbsp;</div>
@@ -42,6 +42,10 @@
<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>
<% } %>
+ <% if(typeof gitlab !== 'undefined' && gitlab) { %>
+ <li role="presentation"><a role="menuitem" class="ui-save-snippet" href="#" data-toggle="modal" data-target="#snippetExportModal"><i class="fa fa-gitlab fa-fw"></i> Snippet</a>
+ </li>
+ <% } %>
<li class="divider"></li>
<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>
@@ -50,6 +54,10 @@
</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) { %>
+ <li role="presentation"><a role="menuitem" class="ui-import-snippet" href="#" data-toggle="modal" data-target="#snippetImportModal"><i class="fa fa-gitlab fa-fw"></i> Snippet</a>
+ </li>
+ <% } %>
<li role="presentation"><a role="menuitem" class="ui-import-clipboard" href="#" data-toggle="modal" data-target="#clipboardModal"><i class="fa fa-clipboard fa-fw"></i> Clipboard</a>
</li>
<li class="divider"></li>
@@ -127,6 +135,10 @@
<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>
<% } %>
+ <% if(typeof gitlab !== 'undefined' && gitlab) { %>
+ <li role="presentation"><a role="menuitem" class="ui-save-snippet" href="#" data-toggle="modal" data-target="#snippetExportModal"><i class="fa fa-gitlab fa-fw"></i> Snippet</a>
+ </li>
+ <% } %>
<li class="divider"></li>
<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>
@@ -135,6 +147,10 @@
</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) { %>
+ <li role="presentation"><a role="menuitem" class="ui-import-snippet" href="#" data-toggle="modal" data-target="#snippetImportModal"><i class="fa fa-gitlab fa-fw"></i> Snippet</a>
+ </li>
+ <% } %>
<li role="presentation"><a role="menuitem" class="ui-import-clipboard" href="#" data-toggle="modal" data-target="#clipboardModal"><i class="fa fa-clipboard fa-fw"></i> Clipboard</a>
</li>
<li class="divider"></li>
@@ -148,4 +164,4 @@
</ul>
</div>
</nav>
-<div class="ui-spinner unselectable hidden-print"></div> \ No newline at end of file
+<div class="ui-spinner unselectable hidden-print"></div>
diff --git a/public/views/index.ejs b/public/views/index.ejs
index c8468848..98641df6 100644
--- a/public/views/index.ejs
+++ b/public/views/index.ejs
@@ -15,7 +15,7 @@
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
<!-- Bootstrap core CSS -->
- <% if(useCDN) { %>
+ <% if(useCDN) { %>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.9.0/bootstrap-social.min.css">
@@ -58,8 +58,10 @@
<p class="lead">
Realtime collaborative markdown notes on all platforms.
</p>
+ <% if(facebook || twitter || github || gitlab || dropbox) { %>
<a type="button" class="btn btn-lg btn-success ui-signin" data-toggle="modal" data-target=".signin-modal" style="display:none;">Sign In</a>
<div class="ui-or" style="display:none;">Or</div>
+ <% }%>
<p class="lead">
<a href="<%- url %>/new" class="btn btn-lg btn-default">New note</a>
</p>
@@ -70,6 +72,7 @@
</div>
<div id="history" class="section" style="display:none;">
+ <% if(facebook || twitter || github || gitlab || dropbox) { %>
<div class="ui-signin">
<h4>
<a type="button" class="btn btn-success" data-toggle="modal" data-target=".signin-modal">Sign In</a> to get own history!
@@ -82,6 +85,7 @@
<a href="<%- url %>/new" class="btn btn-default">New note</a> Or
<a href="#" class="btn btn-danger ui-logout">Sign Out</a>
</div>
+ <% }%>
<hr>
<form class="form-inline">
<div class="form-group" style="vertical-align: bottom;">
@@ -180,25 +184,25 @@
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
- <% if(useCDN) { %>
- <script src="//code.jquery.com/jquery-1.11.3.min.js" defer></script>
- <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" defer></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js" defer></script>
+ <% if(useCDN) { %>
+ <script src="//code.jquery.com/jquery-1.11.3.min.js" defer></script>
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" defer></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js" defer></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/jquery.gsap.min.js" defer></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js" defer></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment-with-locales.min.js" defer></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/js-url/2.0.2/url.min.js" defer></script>
- <% } else { %>
- <script src="<%- url %>/vendor/jquery/dist/jquery.min.js" defer></script>
- <script src="<%- url %>/vendor/bootstrap/dist/js/bootstrap.min.js" defer></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js" defer></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment-with-locales.min.js" defer></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/js-url/2.0.2/url.min.js" defer></script>
+ <% } else { %>
+ <script src="<%- url %>/vendor/jquery/dist/jquery.min.js" defer></script>
+ <script src="<%- url %>/vendor/bootstrap/dist/js/bootstrap.min.js" defer></script>
<script src="<%- url %>/vendor/gsap/src/minified/TweenMax.min.js" defer></script>
<script src="<%- url %>/vendor/gsap/src/minified/jquery.gsap.min.js" defer></script>
- <script src="<%- url %>/vendor/select2/select2.min.js" defer></script>
- <script src="<%- url %>/vendor/moment/min/moment-with-locales.min.js" defer></script>
+ <script src="<%- url %>/vendor/select2/select2.min.js" defer></script>
+ <script src="<%- url %>/vendor/moment/min/moment-with-locales.min.js" defer></script>
<script src="<%- url %>/vendor/handlebars/handlebars.min.js" defer></script>
- <script src="<%- url %>/vendor/js-url/url.min.js" defer></script>
- <% } %>
+ <script src="<%- url %>/vendor/js-url/url.min.js" defer></script>
+ <% } %>
<script src="<%- url %>/vendor/js.cookie.js" defer></script>
<script src="<%- url %>/vendor/list.min.js" defer></script>
<script src="<%- url %>/vendor/FileSaver.min.js" defer></script>
@@ -209,4 +213,4 @@
<script src="<%- url %>/js/cover.js" defer></script>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/public/views/signin-modal.ejs b/public/views/signin-modal.ejs
index 260ff423..4eb33bf3 100644
--- a/public/views/signin-modal.ejs
+++ b/public/views/signin-modal.ejs
@@ -28,6 +28,11 @@
<i class="fa fa-dropbox"></i> Sign in via Dropbox
</a>
<% } %>
+ <% if(gitlab) { %>
+ <a href="<%- url %>/auth/gitlab" class="btn btn-lg btn-block btn-social btn-soundcloud">
+ <i class="fa fa-gitlab"></i> Sign in via GitLab
+ </a>
+ <% } %>
</div>
</div>
</div>