diff options
29 files changed, 287 insertions, 169 deletions
diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..2c3d6b04 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +lib/ot +public/vendor +public/build diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..b826a37b --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + "root": true, + "extends": "standard", + "env": { + "node": true + }, + "rules": { + // at some point all of these should return to their default "error" state + // but right now, this is not a good choice, because too many places are + // wrong. + "import/first": ["warn"], + "indent": ["warn"], + "no-multiple-empty-lines": ["warn"], + "no-multi-spaces": ["warn"], + "object-curly-spacing": ["warn"], + "one-var": ["warn"], + "quotes": ["warn"], + "semi": ["warn"], + "space-infix-ops": ["warn"] + } +}; @@ -1,8 +1,6 @@ CodiMD === -[![Standard - JavaScript Style Guide][standardjs-image]][standardjs-url] - [![#CodiMD on matrix.org][matrix.org-image]][matrix.org-url] [![build status][travis-image]][travis-url] [![version][github-version-badge]][github-release-page] @@ -187,6 +185,7 @@ There are some config settings you need to change in the files below. | `CMD_ALLOW_ANONYMOUS` | `true` or `false` | set to allow anonymous usage (default is `true`) | | `CMD_ALLOW_ANONYMOUS_EDITS` | `true` or `false` | if `allowAnonymous` is `true`, allow users to select `freely` permission, allowing guests to edit existing notes (default is `false`) | | `CMD_ALLOW_FREEURL` | `true` or `false` | set to allow new note creation by accessing a nonexistent note URL | +| `CMD_FORBIDDEN_NODE_IDS` | `'robots.txt'` | disallow creation of notes, even if `CMD_ALLOW_FREEURL` is `true` | | `CMD_DEFAULT_PERMISSION` | `freely`, `editable`, `limited`, `locked` or `private` | set notes default permission (only applied on signed users) | | `CMD_DB_URL` | `mysql://localhost:3306/database` | set the database URL | | `CMD_SESSION_SECRET` | no example | Secret used to sign the session cookie. If non is set, one will randomly generated on startup | @@ -286,6 +285,7 @@ There are some config settings you need to change in the files below. | `allowAnonymous` | `true` or `false` | set to allow anonymous usage (default is `true`) | | `allowAnonymousEdits` | `true` or `false` | if `allowAnonymous` is `true`: allow users to select `freely` permission, allowing guests to edit existing notes (default is `false`) | | `allowFreeURL` | `true` or `false` | set to allow new note creation by accessing a nonexistent note URL | +| `forbiddenNoteIDs` | `['robots.txt']` | disallow creation of notes, even if `allowFreeUrl` is `true` | | `defaultPermission` | `freely`, `editable`, `limited`, `locked`, `protected` or `private` | set notes default permission (only applied on signed users) | | `dbURL` | `mysql://localhost:3306/database` | set the db URL; if set, then db config (below) won't be applied | | `db` | `{ "dialect": "sqlite", "storage": "./db.codimd.sqlite" }` | set the db configs, [see more here](http://sequelize.readthedocs.org/en/latest/api/sequelize/) | @@ -373,7 +373,5 @@ See more at [http://operational-transformation.github.io/](http://operational-tr [travis-url]: https://travis-ci.org/hackmdio/codimd [github-version-badge]: https://img.shields.io/github/release/hackmdio/codimd.svg [github-release-page]: https://github.com/hackmdio/codimd/releases -[standardjs-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg -[standardjs-url]: https://github.com/feross/standard [poeditor-image]: https://img.shields.io/badge/POEditor-translate-blue.svg [poeditor-url]: https://poeditor.com/join/project/1OpGjF2Jir @@ -53,7 +53,7 @@ if (config.useSSL) { // logger app.use(morgan('combined', { - 'stream': logger + 'stream': logger.stream })) // socket io @@ -8,11 +8,12 @@ if [ -d .git ]; then cd "$(git rev-parse --show-toplevel)" fi -if ! type npm > /dev/null +if ! type yarn > /dev/null then cat << EOF -npm is not installed, please install Node.js and npm. +yarn is not installed, please install Node.js, npm and yarn. Read more on Node.js official website: https://nodejs.org +And for yarn package manager at: https://yarnpkg.com/en/ Setup will not be run EOF exit 0 @@ -27,8 +28,9 @@ if [ ! -f .sequelizerc ]; then cp .sequelizerc.example .sequelizerc fi -echo "install npm packages" -BUILD_ASSETS=false npm install +echo "install packages" +yarn install --pure-lockfile +yarn install --production=false --pure-lockfile cat << EOF diff --git a/lib/config/default.js b/lib/config/default.js index 71375b98..d7a8f471 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -32,6 +32,7 @@ module.exports = { allowAnonymous: true, allowAnonymousEdits: false, allowFreeURL: false, + forbiddenNoteIDs: ['robots.txt', 'favicon.ico', 'api'], defaultPermission: 'editable', dbURL: '', db: {}, diff --git a/lib/config/environment.js b/lib/config/environment.js index 4220e54d..a57fe0db 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -28,6 +28,7 @@ module.exports = { allowAnonymous: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS), allowAnonymousEdits: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_EDITS), allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL), + forbiddenNoteIDs: toArrayConfig(process.env.CMD_FORBIDDEN_NOTE_IDS), defaultPermission: process.env.CMD_DEFAULT_PERMISSION, dbURL: process.env.CMD_DB_URL, sessionSecret: process.env.CMD_SESSION_SECRET, diff --git a/lib/logger.js b/lib/logger.js index c70b81b8..5ef1860a 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,7 +1,7 @@ 'use strict' const {createLogger, format, transports} = require('winston') -module.exports = createLogger({ +const logger = createLogger({ level: 'debug', format: format.combine( format.uncolorize(), @@ -17,3 +17,11 @@ module.exports = createLogger({ ], exitOnError: false }) + +logger.stream = { + write: function (message, encoding) { + logger.info(message) + } +} + +module.exports = logger diff --git a/lib/migrations/20150702001020-update-to-0_3_1.js b/lib/migrations/20150702001020-update-to-0_3_1.js index eb18211f..e1a88661 100644 --- a/lib/migrations/20150702001020-update-to-0_3_1.js +++ b/lib/migrations/20150702001020-update-to-0_3_1.js @@ -21,7 +21,7 @@ module.exports = { defaultValue: 0 }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'" || error.message === 'column "shortid" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: shortid' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'" || error.message === 'column "shortid" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20160112220142-note-add-lastchange.js b/lib/migrations/20160112220142-note-add-lastchange.js index 682337c9..87e3ff19 100644 --- a/lib/migrations/20160112220142-note-add-lastchange.js +++ b/lib/migrations/20160112220142-note-add-lastchange.js @@ -8,7 +8,7 @@ module.exports = { type: Sequelize.DATE }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'" || error.message === 'column "lastchangeuserId" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: lastchangeuserId' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'" || error.message === 'column "lastchangeuserId" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20160420180355-note-add-alias.js b/lib/migrations/20160420180355-note-add-alias.js index 6cc1337e..45d53e69 100644 --- a/lib/migrations/20160420180355-note-add-alias.js +++ b/lib/migrations/20160420180355-note-add-alias.js @@ -8,7 +8,7 @@ module.exports = { indicesType: 'UNIQUE' }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'" || error.message === 'column "alias" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: alias' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'" || error.message === 'column "alias" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20160515114000-user-add-tokens.js b/lib/migrations/20160515114000-user-add-tokens.js index 8bf6d11f..435ae9cb 100644 --- a/lib/migrations/20160515114000-user-add-tokens.js +++ b/lib/migrations/20160515114000-user-add-tokens.js @@ -4,7 +4,7 @@ module.exports = { return queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING).then(function () { return queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'" || error.message === 'column "accessToken" of relation "Users" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: accessToken' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'" || error.message === 'column "accessToken" of relation "Users" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20160607060246-support-revision.js b/lib/migrations/20160607060246-support-revision.js index 465a09fa..547f89b8 100644 --- a/lib/migrations/20160607060246-support-revision.js +++ b/lib/migrations/20160607060246-support-revision.js @@ -16,7 +16,7 @@ module.exports = { updatedAt: Sequelize.DATE }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'" || error.message === 'column "savedAt" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: savedAt' | error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'" || error.message === 'column "savedAt" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20160703062241-support-authorship.js b/lib/migrations/20160703062241-support-authorship.js index ccdfeb3e..f452b1a7 100644 --- a/lib/migrations/20160703062241-support-authorship.js +++ b/lib/migrations/20160703062241-support-authorship.js @@ -17,7 +17,7 @@ module.exports = { updatedAt: Sequelize.DATE }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'" || error.message === 'column "authorship" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: authorship' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'" || error.message === 'column "authorship" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20161009040430-support-delete-note.js b/lib/migrations/20161009040430-support-delete-note.js index 39e6f7fb..56a336ac 100644 --- a/lib/migrations/20161009040430-support-delete-note.js +++ b/lib/migrations/20161009040430-support-delete-note.js @@ -2,7 +2,7 @@ module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'" || error.message === 'column "deletedAt" of relation "Notes" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: deletedAt' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'" || error.message === 'column "deletedAt" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/migrations/20161201050312-support-email-signin.js b/lib/migrations/20161201050312-support-email-signin.js index 0a8a832d..26bc09ea 100644 --- a/lib/migrations/20161201050312-support-email-signin.js +++ b/lib/migrations/20161201050312-support-email-signin.js @@ -10,7 +10,7 @@ module.exports = { } }) }).catch(function (error) { - if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'" || error.message === 'column "email" of relation "Users" already exists') { + if (error.message === 'SQLITE_ERROR: duplicate column name: email' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'" || error.message === 'column "email" of relation "Users" already exists') { console.log('Migration has already run… ignoring.') } else { throw error diff --git a/lib/models/index.js b/lib/models/index.js index 0a44ca87..ef70475e 100644 --- a/lib/models/index.js +++ b/lib/models/index.js @@ -25,6 +25,7 @@ if (config.dbURL) { // https://github.com/sequelize/sequelize/issues/6485 function stripNullByte (value) { value = '' + value + // eslint-disable-next-line no-control-regex return value ? value.replace(/\u0000/g, '') : value } sequelize.stripNullByte = stripNullByte diff --git a/lib/models/user.js b/lib/models/user.js index 1bd8c745..2ebf6d06 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -50,7 +50,7 @@ module.exports = function (sequelize, DataTypes) { }, { instanceMethods: { verifyPassword: function (attempt) { - if (scrypt.verifyKdfSync(new Buffer(this.password, 'hex'), attempt)) { + if (scrypt.verifyKdfSync(Buffer.from(this.password, 'hex'), attempt)) { return this } else { return false diff --git a/lib/realtime.js b/lib/realtime.js index 8541bafa..d04ffdc2 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -242,6 +242,7 @@ function getStatus (callback) { } }) models.User.count().then(function (regcount) { + // eslint-disable-next-line standard/no-callback-literal return callback ? callback({ onlineNotes: Object.keys(notes).length, onlineUsers: Object.keys(users).length, @@ -283,7 +284,7 @@ function extractNoteIdFromSocket (socket) { if (!referer) { return false } - var hostUrl = url.parse(referer) + var hostUrl = url.URL.parse(referer) var noteId = config.urlPath ? hostUrl.pathname.slice(config.urlPath.length + 1, hostUrl.pathname.length).split('/')[1] : hostUrl.pathname.split('/')[1] return noteId } else { diff --git a/lib/response.js b/lib/response.js index 671aa120..b94f473a 100644 --- a/lib/response.js +++ b/lib/response.js @@ -157,7 +157,7 @@ function findNote (req, res, callback, include) { include: include || null }).then(function (note) { if (!note) { - if (config.allowFreeURL && noteId) { + if (config.allowFreeURL && noteId && !config.forbiddenNoteIDs.includes(noteId)) { req.alias = noteId return newNote(req, res) } else { diff --git a/lib/web/auth/oauth2/index.js b/lib/web/auth/oauth2/index.js index f2a3132d..b9160f6e 100644 --- a/lib/web/auth/oauth2/index.js +++ b/lib/web/auth/oauth2/index.js @@ -2,13 +2,13 @@ const Router = require('express').Router const passport = require('passport') -const OAuth2Strategy = require('passport-oauth2').Strategy +const { Strategy, InternalOAuthError } = require('passport-oauth2') const config = require('../../../config') const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') let oauth2Auth = module.exports = Router() -class OAuth2CustomStrategy extends OAuth2Strategy { +class OAuth2CustomStrategy extends Strategy { constructor (options, verify) { options.customHeaders = options.customHeaders || {} super(options, verify) @@ -22,7 +22,7 @@ class OAuth2CustomStrategy extends OAuth2Strategy { var json if (err) { - return done(new passport.InternalOAuthError('Failed to fetch user profile', err)) + return done(new InternalOAuthError('Failed to fetch user profile', err)) } try { @@ -67,7 +67,7 @@ OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) { var json if (err) { - return done(new passport.InternalOAuthError('Failed to fetch user profile', err)) + return done(new InternalOAuthError('Failed to fetch user profile', err)) } try { diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js index 8c432b0c..a2f8700d 100644 --- a/lib/web/imageRouter/filesystem.js +++ b/lib/web/imageRouter/filesystem.js @@ -16,5 +16,5 @@ exports.uploadImage = function (imagePath, callback) { return } - callback(null, url.resolve(config.serverURL + '/uploads/', path.basename(imagePath))) + callback(null, url.URL.resolve(config.serverURL + '/uploads/', path.basename(imagePath))) } diff --git a/locales/nl.json b/locales/nl.json index 325c273d..6fd857a7 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -1,106 +1,179 @@ { - "Collaborative markdown notes": "Samenwerkende markdown notities", - "Realtime collaborative markdown notes on all platforms.": "Realtime samenwerkende markdown notities.", - "Best way to write and share your knowledge in markdown.": "De beste manier je kennis te delen en schrijven in markdown.", - "Intro": "Introductie", - "History": "Geschiedenis", - "New guest note": "Nieuwe gast notitie", - "Collaborate with URL": "Samenwerken met URL", - "Support charts and MathJax": "Ondersteun grafieken en MathJax", - "Support slide mode": "Ondersteun slide mode", - "Sign In": "Log in", - "Below is the history from browser": "Hier onder staat de browser geschiedenis", - "Welcome!": "Welkom!", - "New note": "Nieuwe notitie", - "or": "of", - "Sign Out": "Log uit", - "Explore all features": "Ontdek alle features", - "Select tags...": "Selecteer tags...", - "Search keyword...": "Zoeken op keyword...", - "Sort by title": "Sorteren op titel", - "Title": "Titel", - "Sort by time": "Sorteren op tijd", - "Time": "Tijd", - "Export history": "Exporteer geschiedenis", - "Import history": "Importeer geschiedenis", - "Clear history": "Verwijder geschiedenis", - "Refresh history": "Ververs geschiedenis", - "No history": "Geen geschidenis gevonden", - "Import from browser": "Importeer van browser", - "Releases": "Releases", - "Are you sure?": "Weet je het zeker?", - "Do you really want to delete this note?": "Will je deze notitie echt verwijderen?", - "All users will lose their connection.": "Alle gebruikers zullen hun verbinding verliezen.", - "Cancel": "Stoppen", - "Yes, do it!": "Ja, doe het!", - "Choose method": "Kies methode", - "Sign in via %s": "Log in via %s", - "New": "Nieuw", - "Publish": "Publiceren", - "Extra": "Extra", - "Revision": "Herzien", - "Slide Mode": "Slide Mode", - "Export": "Exporteren", - "Import": "Importeren", - "Clipboard": "Kladbord", - "Download": "Downloaden", - "Raw HTML": "Raw HTML", - "Edit": "Aanpassen", - "View": "Bekijken", - "Both": "Beide", - "Help": "Help", - "Upload Image": "Afbeelding uploaden", - "Menu": "Menu", - "This page need refresh": "Deze pagina moet herladen worden", - "You have an incompatible client version.": "Je client is niet compatible.", - "Refresh to update.": "Ververs om te updaten.", - "New version available!": "Nieuwe versie beschikbaar!", - "See releases notes here": "Zie releases hier", - "Refresh to enjoy new features.": "Ververs om de nieuwe features te zien.", - "Your user state has changed.": "Je gebruikers-status is veranderd.", - "Refresh to load new user state.": "Ververs om je nieuwe gebruikers-status te zien.", - "Refresh": "Ververs", - "Contacts": "Contacten", - "Report an issue": "Probleem rapporteren", - "Send us email": "Stuur ons een mail", - "Documents": "Documenten", - "Features": "Features", - "YAML Metadata": "YAML Metadata", - "Slide Example": "Slide Voorbeeld", - "Cheatsheet": "Afkijkblad", - "Example": "Voorbeeld", - "Syntax": "Syntax", - "Header": "Koptekst", - "Unordered List": "Niet gesorteerde Lijst", - "Ordered List": "Gesorteerde List", - "Todo List": "Todo Lijst", - "Blockquote": "Quote", - "Bold font": "Bold tekst", - "Italics font": "Italics tekst", - "Strikethrough": "Doorstreepte tekst", - "Inserted text": "Bijgevoegde tekst", - "Marked text": "Gemarkeerde tekst", - "Link": "Link", - "Image": "Afbeelding", - "Code": "Code", - "Externals": "Uiterlijkheden", - "This is a alert area.": "Dit is een waarschuwingsgebied.", - "Revert": "Terugzetten", - "Import from clipboard": "Importeren from kladbord", - "Paste your markdown or webpage here...": "Plak je markdown of webpagina hier...", - "Clear": "Legen", - "This note is locked": "Deze notitie zit op slot", - "Sorry, only owner can edit this note.": "Sorry, alleen de eigenaar kan deze notitie aanpassen.", - "OK": "OK", - "Reach the limit": "Limiet bereikt", - "Sorry, you've reached the max length this note can be.": "Sorry, je notitie heeft de maximale lengte bereikt.", - "Please reduce the content or divide it to more notes, thank you!": "Verwijder alsjeblieft wat tekst of verdeel het over meerdere notities!", - "Import from Gist": "Importeren vanaf een Gist", - "Paste your gist url here...": "Plak je Gist URL hier...", - "Import from Snippet": "Imporeren vanaf een Snippet", - "Select From Available Projects": "Selecteer van beschikbare projecten", - "Select From Available Snippets": "Selecteer van beschikbare Snippets", - "OR": "OF", - "Export to Snippet": "Exporteren naar Snippet", - "Select Visibility Level": "Selecteer zichtbaarheids niveau" -} + "Used in the title of every note that has no title set.": { + "Collaborative markdown notes": "Samenwerkende markdown notities" + }, + "Not used right now.": { + "Realtime collaborative markdown notes on all platforms.": "Realtime samenwerkende markdown notities.", + "Welcome!": "Welkom!" + }, + "Subtitle on the entry page. Used to tell what CodiMD is.": { + "Best way to write and share your knowledge in markdown.": "De beste manier je kennis te delen en schrijven in markdown." + }, + "Name of the menu item on the index page. It's the default tab for unauthenticated users.": { + "Intro": "Introductie" + }, + "Name of the tab that shows the user's note history. Default page for logged in users": { + "History": "Geschiedenis" + }, + "Button on the main page to create a new note as guest": { + "New guest note": "Nieuwe gast notitie" + }, + "Little text to illustrate some of our features on the index page. Also links to the share-notes section on the features page": { + "Collaborate with URL": "Samenwerken met URL" + }, + "Little text to illustrate the mathjax feature of CodiMD. Also links to the MathJax section of the features page": { + "Support charts and MathJax": "Ondersteun grafieken en MathJax" + }, + "Shows that CodiMD has a slide mode feature and links to the Slide-mode section of the features page": { + "Support slide mode": "Ondersteun slide mode" + }, + "Login button on the index page": { + "Sign In": "Log in" + }, + "Shows up on the history page for non-logged in users": { + "Below is the history from browser": "Hier onder staat de browser geschiedenis" + }, + "New note\"-button on the index page for logged in users": { + "New note": "Nieuwe notitie" + }, + "Used on the index page for not logged in users to split the \"sign in\" and \"Explore all features\" button.": { + "or": "of" + }, + "Log out string on the index page. In the drop-down menu for the user name.": { + "Sign Out": "Log uit" + }, + "Text in the button that links to the features page": { + "Explore all features": "Ontdek alle features" + }, + "Placeholder text in the search field for tags on the history tab on the index page": { + "Select tags...": "Selecteer tags..." + }, + "placeholder text in the search field for keywords on the history tab on the index page": { + "Search keyword...": "Zoeken op keyword..." + }, + "Help text for the \"Title\"-button on the history tab on the index page.": { + "Sort by title": "Sorteren op titel" + }, + "Text to sort notes by title on the history tab on the index page.": { + "Title": "Titel" + }, + "Sort by time": "Sorteren op tijd", + "Time": "Tijd", + "Export history": "Exporteer geschiedenis", + "Import history": "Importeer geschiedenis", + "Clear history": "Verwijder geschiedenis", + "Refresh history": "Ververs geschiedenis", + "No history": "Geen geschidenis gevonden", + "Import from browser": "Importeer van browser", + "Releases": "Releases", + "Are you sure?": "Weet je het zeker?", + "Do you really want to delete this note?": "Will je deze notitie echt verwijderen?", + "All users will lose their connection.": "Alle gebruikers zullen hun verbinding verliezen.", + "Cancel": "Stoppen", + "Yes, do it!": "Ja, doe het!", + "Choose method": "Kies methode", + "Sign in via %s": "Log in via %s", + "New": "Nieuw", + "Publish": "Publiceren", + "Extra": "Extra", + "Revision": "Versie", + "Slide Mode": "Slide Mode", + "Export": "Exporteren", + "Import": "Importeren", + "Clipboard": "Kladbord", + "Download": "Downloaden", + "Raw HTML": "Ruwe HTML", + "Edit": "Aanpassen", + "View": "Bekijken", + "Both": "Beide", + "Help": "Help", + "Upload Image": "Afbeelding uploaden", + "Menu": "Menu", + "This page need refresh": "Deze pagina moet vernieuwd worden", + "You have an incompatible client version.": "Je client is niet compatibel.", + "Refresh to update.": "Ververs om te updaten.", + "New version available!": "Nieuwe versie beschikbaar!", + "See releases notes here": "Bekijk de release notes hier", + "Refresh to enjoy new features.": "Ververs om de nieuwe features te zien.", + "Your user state has changed.": "Je gebruikers-status is veranderd.", + "Refresh to load new user state.": "Ververs om je nieuwe gebruikers-status te zien.", + "Refresh": "Ververs", + "Contacts": "Contacten", + "Report an issue": "Probleem rapporteren", + "Meet us on %s": "Ontmoet ons op %s", + "Send us email": "Stuur ons een mail", + "Documents": "Documenten", + "Features": "Features", + "YAML Metadata": "YAML Metadata", + "Slide Example": "Slide Voorbeeld", + "Cheatsheet": "Afkijkblad", + "Example": "Voorbeeld", + "Syntax": "Syntax", + "Header": "Koptekst", + "Unordered List": "Niet gesorteerde Lijst", + "Ordered List": "Gesorteerde List", + "Todo List": "Todo Lijst", + "Blockquote": "Citaat", + "Bold font": "Bold tekst", + "Italics font": "Italics tekst", + "Strikethrough": "Doorstreepte tekst", + "Inserted text": "Bijgevoegde tekst", + "Marked text": "Gemarkeerde tekst", + "Link": "Link", + "Image": "Afbeelding", + "Code": "Code", + "Externals": "Uiterlijkheden", + "This is a alert area.": "Dit is een waarschuwingsgebied.", + "Revert": "Terugzetten", + "Import from clipboard": "Importeren from kladbord", + "Paste your markdown or webpage here...": "Plak je markdown of webpagina hier...", + "Clear": "Legen", + "This note is locked": "Deze notitie zit op slot", + "Sorry, only owner can edit this note.": "Sorry, alleen de eigenaar kan deze notitie aanpassen.", + "OK": "OK", + "Reach the limit": "Limiet bereikt", + "Sorry, you've reached the max length this note can be.": "Sorry, je notitie heeft de maximale lengte bereikt.", + "Please reduce the content or divide it to more notes, thank you!": "Verwijder alsjeblieft wat tekst of verdeel het over meerdere notities!", + "Import from Gist": "Importeren vanaf een Gist", + "Paste your gist url here...": "Plak je Gist URL hier...", + "Import from Snippet": "Imporeren vanaf een Snippet", + "Select From Available Projects": "Selecteer van beschikbare projecten", + "Select From Available Snippets": "Selecteer van beschikbare Snippets", + "OR": "OF", + "Used in the drop-down menu in the codimd view. Allows to export notes to GitLab Snippets.": { + "Export to Snippet": "Exporteren naar Snippet" + }, + "Used in the GitLab snippet export and import menu in the codimd view.": { + "Select Visibility Level": "Selecteer zichtbaarheids niveau" + }, + "Addtional text that shows up by hovering over the night mode button in the codimd view. Should explain what the moon sign at the top does.": { + "Night Theme": "Nachtweergave" + }, + "Text at the bottom of the index page. Contains 3 links to github, Matrix and POEditor.\n\nLike: \"Follow us on Github, Matrix and POEditor.": { + "Follow us on %s and %s.": "Volg ons op %s en %s." + }, + "Optional text at the bottom of the index page linking to the privacy policy of an instance.": { + "Privacy": "Privacy" + }, + "Optional text at the bottom of the index page linking to the Terms of Use document provided by the instance.": { + "Terms of Use": "Gebruikersvoorwaarden" + }, + "Used as title of the modal that asks the user that he/she is really sure about deleting his/her account.": { + "Do you really want to delete your user account?": "Weet je zeker dat je je account wilt verwijderen?" + }, + "Used in a modal that shows up as soon as the user clicks on the \"Delete user\"-button in the user drop-down menu. Should make clear that everything the user owns or references to the user will be deleted and can not be recovered.": { + "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Dit zal je account verwijderen. Alle notities waar je eigenaar van bent worden verwijderd, samen met alle verwijzingen naar je account." + }, + "Used in the user drop-down menu on the index page. Opens a modal that informs the user that his account will be deleted completely and makes sure the user really want that. Then deletes the account including all notes a user owns.": { + "Delete user": "Gebruiker verwijderen" + }, + "Used in the user drop-down menu on the index page. Generates an archive of all notes a user owns and downloads them.": { + "Export user data": "Gebruikersdata exporteren" + }, + "Used in the sidebar of the help modal. Links to POEditor, our translation platform.": { + "Help us translating on %s": "Help ons vertalen op %s" + }, + "Link besides the \"Release\" and possible \"Privacy policies\" at the bottom of the index page that points to the source code of the CodiMD version you are using.": { + "Source Code": "" + } +}
\ No newline at end of file diff --git a/package.json b/package.json index cd875ac6..2cc9415a 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,14 @@ "main": "app.js", "license": "AGPL-3.0", "scripts": { - "test": "npm run-script standard && npm run-script jsonlint", + "test": "npm run-script eslint && npm run-script jsonlint", + "eslint": "node_modules/.bin/eslint lib public app.js", "jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' -o -type f -name '*.json.example' | while read json; do echo $json ; jq . $json; done", - "standard": "node ./node_modules/standard/bin/cmd.js", + "standard": "echo 'standard is no longer being used, use `npm run eslint` instead!' && exit 1", "dev": "webpack --config webpack.dev.js --progress --colors --watch", "build": "webpack --config webpack.prod.js --progress --colors --bail", "postinstall": "bin/heroku", - "start": "node app.js", + "start": "sequelize db:migrate && node app.js", "doctoc": "doctoc --title='# Table of Contents' README.md" }, "dependencies": { @@ -169,6 +170,12 @@ "css-loader": "^1.0.0", "doctoc": "^1.3.0", "ejs-loader": "^0.3.1", + "eslint": "^5.9.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-promise": "^4.0.1", + "eslint-plugin-standard": "^4.0.0", "exports-loader": "^0.7.0", "expose-loader": "^0.7.5", "file-loader": "^2.0.0", @@ -180,7 +187,6 @@ "mini-css-extract-plugin": "^0.4.1", "optimize-css-assets-webpack-plugin": "^5.0.0", "script-loader": "^0.7.2", - "standard": "^9.0.1", "string-loader": "^0.0.1", "style-loader": "^0.21.0", "uglifyjs-webpack-plugin": "^1.2.7", @@ -190,34 +196,6 @@ "webpack-merge": "^4.1.4", "webpack-parallel-uglify-plugin": "^1.1.0" }, - "standard": { - "globals": [ - "$", - "CodeMirror", - "Cookies", - "moment", - "editor", - "ui", - "Spinner", - "modeType", - "Idle", - "serverurl", - "key", - "gapi", - "Dropbox", - "FilePicker", - "ot", - "MediaUploader", - "hex2rgb", - "num_loaded", - "Visibility", - "inlineAttachment" - ], - "ignore": [ - "lib/ot", - "public/vendor" - ] - }, "optionalDependencies": { "bufferutil": "^4.0.0", "utf-8-validate": "^5.0.1" diff --git a/public/.eslintrc.js b/public/.eslintrc.js new file mode 100644 index 00000000..dc37b3cb --- /dev/null +++ b/public/.eslintrc.js @@ -0,0 +1,28 @@ +// this config file is used in concert with the root .eslintrc.js in the root dir. +module.exports = { + "env": { + "browser": true + }, + "globals": { + "$": false, + "CodeMirror": false, + "Cookies": false, + "moment": false, + "editor": false, + "ui": false, + "Spinner": false, + "modeType": false, + "Idle": false, + "serverurl": false, + "key": false, + "gapi": false, + "Dropbox": false, + "FilePicker": false, + "ot": false, + "MediaUploader": false, + "hex2rgb": false, + "num_loaded": false, + "Visibility": false, + "inlineAttachment": false + } +}; diff --git a/public/js/history.js b/public/js/history.js index b4c26b42..6007bef4 100644 --- a/public/js/history.js +++ b/public/js/history.js @@ -218,6 +218,7 @@ export function getStorageHistory (callback) { if (typeof data === 'string') { data = JSON.parse(data) } callback(data) } + // eslint-disable-next-line standard/no-callback-literal callback([]) } diff --git a/public/js/index.js b/public/js/index.js index 98c3b6d2..0c575961 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2511,7 +2511,9 @@ function buildCursor (user) { // editor actions function removeNullByte (cm, change) { var str = change.text.join('\n') + // eslint-disable-next-line no-control-regex if (/\u0000/g.test(str) && change.update) { + // eslint-disable-next-line no-control-regex change.update(change.from, change.to, str.replace(/\u0000/g, '').split('\n')) } } @@ -3046,7 +3048,7 @@ function checkInCode () { function checkAbove (method) { var cursor = editor.getCursor() var text = [] - for (var i = 0; i < cursor.line; i++) { // contain current line + for (var i = 0; i < cursor.line; i++) { // contain current line text.push(editor.getLine(i)) } text = text.join('\n') + '\n' + editor.getLine(cursor.line).slice(0, cursor.ch) diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index 0537e927..f05d01b8 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -492,7 +492,7 @@ export default class Editor { clearInterval(spellcheckTimer) } }, - 100, + 100 ) } } @@ -514,7 +514,7 @@ export default class Editor { } setOverrideBrowserKeymap () { var overrideBrowserKeymap = $( - '.ui-preferences-override-browser-keymap label > input[type="checkbox"]', + '.ui-preferences-override-browser-keymap label > input[type="checkbox"]' ) if (overrideBrowserKeymap.is(':checked')) { Cookies.set('preferences-override-browser-keymap', true, { @@ -529,10 +529,10 @@ export default class Editor { setPreferences () { var overrideBrowserKeymap = $( - '.ui-preferences-override-browser-keymap label > input[type="checkbox"]', + '.ui-preferences-override-browser-keymap label > input[type="checkbox"]' ) var cookieOverrideBrowserKeymap = Cookies.get( - 'preferences-override-browser-keymap', + 'preferences-override-browser-keymap' ) if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') { overrideBrowserKeymap.prop('checked', true) diff --git a/public/js/lib/editor/ui-elements.js b/public/js/lib/editor/ui-elements.js index ca06d30c..29a37782 100644 --- a/public/js/lib/editor/ui-elements.js +++ b/public/js/lib/editor/ui-elements.js @@ -67,7 +67,7 @@ export const getUIElements = () => ({ codemirrorScroll: $('.ui-edit-area .CodeMirror .CodeMirror-scroll'), codemirrorSizer: $('.ui-edit-area .CodeMirror .CodeMirror-sizer'), codemirrorSizerInner: $( - '.ui-edit-area .CodeMirror .CodeMirror-sizer > div', + '.ui-edit-area .CodeMirror .CodeMirror-sizer > div' ), markdown: $('.ui-view-area .markdown-body'), resize: { |