diff options
-rw-r--r-- | .sequelizerc | 8 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | app.js | 44 | ||||
-rw-r--r-- | config.json | 5 | ||||
-rw-r--r-- | lib/auth.js | 28 | ||||
-rw-r--r-- | lib/config.js | 6 | ||||
-rw-r--r-- | lib/migrations/20160515114000-user-add-tokens.js | 15 | ||||
-rw-r--r-- | lib/models/user.js | 11 | ||||
-rw-r--r-- | lib/response.js | 16 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | public/css/index.css | 5 | ||||
-rw-r--r-- | public/js/common.js | 6 | ||||
-rw-r--r-- | public/js/index.js | 227 | ||||
-rw-r--r-- | public/views/body.ejs | 83 | ||||
-rw-r--r-- | public/views/foot.ejs | 5 | ||||
-rw-r--r-- | public/views/header.ejs | 20 | ||||
-rw-r--r-- | public/views/index.ejs | 38 | ||||
-rw-r--r-- | public/views/signin-modal.ejs | 5 |
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 @@ -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 --- @@ -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">×</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">×</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"> </div> <div class="visible-sm"> </div> <div class="visible-md"> </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> |