diff --git a/app.js b/app.js index 4980d45d02..4b8499d78e 100644 --- a/app.js +++ b/app.js @@ -196,6 +196,7 @@ app.locals.serverURL = config.serverURL app.locals.sourceURL = config.sourceURL app.locals.allowAnonymous = config.allowAnonymous app.locals.allowAnonymousEdits = config.allowAnonymousEdits +app.locals.allowVisibleSource = config.allowVisibleSource app.locals.permission = config.permission app.locals.allowPDFExport = config.allowPDFExport app.locals.authProviders = { diff --git a/lib/config/default.js b/lib/config/default.js index 95ee1940fe..473adaa681 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -33,6 +33,7 @@ module.exports = { allowAnonymousEdits: true, allowAnonymousViews: true, allowFreeURL: false, + allowVisibleSource: false, forbiddenNoteIDs: ['robots.txt', 'favicon.ico', 'api'], defaultPermission: 'editable', dbURL: '', diff --git a/lib/config/environment.js b/lib/config/environment.js index 0867aecf54..0b9a821aab 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -29,6 +29,7 @@ module.exports = { allowAnonymousEdits: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_EDITS), allowAnonymousViews: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_VIEWS), allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL), + allowVisibleSource: toBooleanConfig(process.env.CMD_ALLOW_VISIBLE_SOURCE), forbiddenNoteIDs: toArrayConfig(process.env.CMD_FORBIDDEN_NOTE_IDS), defaultPermission: process.env.CMD_DEFAULT_PERMISSION, dbURL: process.env.CMD_DB_URL, diff --git a/locales/en.json b/locales/en.json index b2883192f1..0f9f4a06af 100644 --- a/locales/en.json +++ b/locales/en.json @@ -117,5 +117,6 @@ "Powered by %s": "Powered by %s", "Register": "Register", "Export with pandoc": "Export with pandoc", - "Select output format": "Select output format" + "Select output format": "Select output format", + "You have no rights to edit this note": "You have no rights to edit this note" } \ No newline at end of file diff --git a/locales/ru.json b/locales/ru.json index 36f65ef44e..357f6c8e94 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,117 +1,122 @@ { - "Collaborative markdown notes": "Совместные markdown заметки", - "Realtime collaborative markdown notes on all platforms.": "Совместные markdown заметки в режиме реального времени на всех платформах.", - "Best way to write and share your knowledge in markdown.": "Лучший способ записывать свои знания и делиться ими в формате markdown.", - "Intro": "Введение", - "History": "История", - "New guest note": "Новая гостевая заметка", - "Collaborate with URL": "Сотрудничество по ссылке", - "Support charts and MathJax": "Поддержка графиков и MathJax", - "Support slide mode": "Поддержка режима слайдера", - "Sign In": "Войти", - "Below is the history from browser": "Ниже приводится история браузера", - "Welcome!": "Добро пожаловать!", - "New note": "Новая заметка", - "or": "или", - "Sign Out": "Выйти", - "Explore all features": "Изучите все возможности", - "Select tags...": "Выберите теги...", - "Search keyword...": "Поиск...", - "Sort by title": "Сортировка по заголовку", - "Title": "Заголовок", - "Sort by time": "Сортировка по времени", - "Time": "Время", - "Export history": "Экспорт истории", - "Import history": "Импорт истории", - "Clear history": "Очистить историю", - "Refresh history": "Обновить историю", - "No history": "Нет истории", - "Import from browser": "Импорт из браузера", - "Releases": "Релизы", - "Are you sure?": "Вы уверены?", - "Do you really want to delete this note?": "Вы точно хотите удалить эту заметку?", - "All users will lose their connection.": "Все пользователи потеряют соединение.", - "Cancel": "Отмена", - "Yes, do it!": "Да, сделать это!", - "Choose method": "Выберите метод", - "Sign in via %s": "Войти с помощью %s", - "New": "Новая", - "Publish": "Опубликовать", - "Extra": "Дополнительно", - "Revision": "Изменения", - "Slide Mode": "Режим слайдера", - "Export": "Экспорт", - "Import": "Импорт", - "Clipboard": "Буфер обмена", - "Download": "Скачать", - "Raw HTML": "Raw HTML", - "Edit": "Редактировать", - "View": "Посмотреть", - "Both": "И то и другое", - "Help": "Помощь", - "Upload Image": "Загрузить изображение", - "Menu": "Меню", - "This page need refresh": "Эту страницу необходимо обновить", - "You have an incompatible client version.": "Вы используете несовместимую версию клиента.", - "Refresh to update.": "Обновите страницу для обновления клиента.", - "New version available!": "Доступна новая версия!", - "See releases notes here": "Смотрите подробности обновлений здесь", - "Refresh to enjoy new features.": "Обновите, чтобы наслаждаться новыми возможностями.", - "Your user state has changed.": "Ваш аккаунт изменен.", - "Refresh to load new user state.": "Обновите, чтобы загрузить изменения аккаунта.", - "Refresh": "Обновить", - "Contacts": "Контакты", - "Report an issue": "Сообщить о проблеме", - "Meet us on %s": "Познакомьтесь с нами в %s", - "Send us email": "Отправить нам письмо", - "Documents": "Документы", - "Features": "Особенности", - "YAML Metadata": "Метаданные YAML", - "Slide Example": "Пример слайдера", - "Cheatsheet": "Шпаргалка", - "Example": "Пример", - "Syntax": "Синтаксис", - "Header": "Заголовок", - "Unordered List": "Маркированный список", - "Ordered List": "Нумерованный список", - "Todo List": "Список дел", - "Blockquote": "Цитата", - "Bold font": "Жирный шрифт", - "Italics font": "Курсив", - "Strikethrough": "Зачеркнутый", - "Inserted text": "Подчеркнутый текст", - "Marked text": "Выделенный текст", - "Link": "Ссылка", - "Image": "Изображение", - "Code": "Код", - "Externals": "Внешнее", - "This is a alert area.": "Это уведомление.", - "Revert": "Отменить", - "Import from clipboard": "Импорт из буфера обмена", - "Paste your markdown or webpage here...": "Вставьте ваш markdown код или веб-страницу здесь...", - "Clear": "Очистить", - "This note is locked": "Эта заметка заблокирована", - "Sorry, only owner can edit this note.": "К сожалению, только автор может редактировать эту заметку.", - "OK": "OK", - "Reach the limit": "Вы достигли лимита", - "Sorry, you've reached the max length this note can be.": "К сожалению, вы достигли максимальной длины заметки.", - "Please reduce the content or divide it to more notes, thank you!": "Пожалуйста, уменьшите размер содержимого или разделите его на несколько заметок, спасибо!", - "Import from Gist": "Импорт из Gist", - "Paste your gist url here...": "Вставьте ссылку на ваш gist здесь...", - "Import from Snippet": "Импорт фрагмента кода", - "Select From Available Projects": "Выберите из доступных проектов", - "Select From Available Snippets": "Выберите из доступных фрагментов кода", - "OR": "ИЛИ", - "Export to Snippet": "Экспорт фрагмента кода", - "Select Visibility Level": "Выберите уровень видимости", - "Night Theme": "Тёмная тема", - "Follow us on %s and %s.": "Подпишитесь на нас в %s и %s.", - "Privacy": "Безопасность", - "Terms of Use": "Условия использования", - "Do you really want to delete your user account?": "Вы точно хотите удалить свою учётную запись?", - "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Это действие удалит вашу учётную запись, все ваши заметки и удалит все ссылки на вашу учетную запись из других заметок.", - "Delete user": "Удалить пользователя", - "Export user data": "Экспортировать данные пользователя", - "Help us translating on %s": "Помогите нам перевести %s", - "Source Code": "Исходный код" + "Collaborative markdown notes": "Совместные markdown заметки", + "Realtime collaborative markdown notes on all platforms.": "Совместные markdown заметки в режиме реального времени на всех платформах.", + "Best way to write and share your knowledge in markdown.": "Лучший способ записывать свои знания и делиться ими в формате markdown.", + "Intro": "Введение", + "History": "История", + "New guest note": "Новая гостевая заметка", + "Collaborate with URL": "Сотрудничество по ссылке", + "Support charts and MathJax": "Поддержка графиков и MathJax", + "Support slide mode": "Поддержка режима слайдера", + "Sign In": "Войти", + "Below is the history from browser": "Ниже приводится история браузера", + "Welcome!": "Добро пожаловать!", + "New note": "Новая заметка", + "or": "или", + "Sign Out": "Выйти", + "Explore all features": "Изучите все возможности", + "Select tags...": "Выберите теги...", + "Search keyword...": "Поиск...", + "Sort by title": "Сортировка по заголовку", + "Title": "Заголовок", + "Sort by time": "Сортировка по времени", + "Time": "Время", + "Export history": "Экспорт истории", + "Import history": "Импорт истории", + "Clear history": "Очистить историю", + "Refresh history": "Обновить историю", + "No history": "Нет истории", + "Import from browser": "Импорт из браузера", + "Releases": "Релизы", + "Are you sure?": "Вы уверены?", + "Do you really want to delete this note?": "Вы точно хотите удалить эту заметку?", + "All users will lose their connection.": "Все пользователи потеряют соединение.", + "Cancel": "Отмена", + "Yes, do it!": "Да, сделать это!", + "Choose method": "Выберите метод", + "Sign in via %s": "Войти с помощью %s", + "New": "Новая", + "Publish": "Опубликовать", + "Extra": "Дополнительно", + "Revision": "Изменения", + "Slide Mode": "Режим слайдера", + "Export": "Экспорт", + "Import": "Импорт", + "Clipboard": "Буфер обмена", + "Download": "Скачать", + "Raw HTML": "Raw HTML", + "Edit": "Редактировать", + "View": "Посмотреть", + "Both": "И то и другое", + "Help": "Помощь", + "Upload Image": "Загрузить изображение", + "Menu": "Меню", + "This page need refresh": "Эту страницу необходимо обновить", + "You have an incompatible client version.": "Вы используете несовместимую версию клиента.", + "Refresh to update.": "Обновите страницу для обновления клиента.", + "New version available!": "Доступна новая версия!", + "See releases notes here": "Смотрите подробности обновлений здесь", + "Refresh to enjoy new features.": "Обновите, чтобы наслаждаться новыми возможностями.", + "Your user state has changed.": "Ваш аккаунт изменен.", + "Refresh to load new user state.": "Обновите, чтобы загрузить изменения аккаунта.", + "Refresh": "Обновить", + "Contacts": "Контакты", + "Report an issue": "Сообщить о проблеме", + "Meet us on %s": "Познакомьтесь с нами в %s", + "Send us email": "Отправить нам письмо", + "Documents": "Документы", + "Features": "Особенности", + "YAML Metadata": "Метаданные YAML", + "Slide Example": "Пример слайдера", + "Cheatsheet": "Шпаргалка", + "Example": "Пример", + "Syntax": "Синтаксис", + "Header": "Заголовок", + "Unordered List": "Маркированный список", + "Ordered List": "Нумерованный список", + "Todo List": "Список дел", + "Blockquote": "Цитата", + "Bold font": "Жирный шрифт", + "Italics font": "Курсив", + "Strikethrough": "Зачеркнутый", + "Inserted text": "Подчеркнутый текст", + "Marked text": "Выделенный текст", + "Link": "Ссылка", + "Image": "Изображение", + "Code": "Код", + "Externals": "Внешнее", + "This is a alert area.": "Это уведомление.", + "Revert": "Отменить", + "Import from clipboard": "Импорт из буфера обмена", + "Paste your markdown or webpage here...": "Вставьте ваш markdown код или веб-страницу здесь...", + "Clear": "Очистить", + "This note is locked": "Эта заметка заблокирована", + "Sorry, only owner can edit this note.": "К сожалению, только автор может редактировать эту заметку.", + "OK": "OK", + "Reach the limit": "Вы достигли лимита", + "Sorry, you've reached the max length this note can be.": "К сожалению, вы достигли максимальной длины заметки.", + "Please reduce the content or divide it to more notes, thank you!": "Пожалуйста, уменьшите размер содержимого или разделите его на несколько заметок, спасибо!", + "Import from Gist": "Импорт из Gist", + "Paste your gist url here...": "Вставьте ссылку на ваш gist здесь...", + "Import from Snippet": "Импорт фрагмента кода", + "Select From Available Projects": "Выберите из доступных проектов", + "Select From Available Snippets": "Выберите из доступных фрагментов кода", + "OR": "ИЛИ", + "Export to Snippet": "Экспорт фрагмента кода", + "Select Visibility Level": "Выберите уровень видимости", + "Night Theme": "Тёмная тема", + "Follow us on %s and %s.": "Подпишитесь на нас в %s и %s.", + "Privacy": "Безопасность", + "Terms of Use": "Условия использования", + "Do you really want to delete your user account?": "Вы точно хотите удалить свою учётную запись?", + "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Это действие удалит вашу учётную запись, все ваши заметки и удалит все ссылки на вашу учетную запись из других заметок.", + "Delete user": "Удалить пользователя", + "Export user data": "Экспортировать данные пользователя", + "Help us translating on %s": "Помогите нам перевести %s", + "Source Code": "Исходный код", + "Powered by %s": "Powered by %s", + "Register": "Register", + "Export with pandoc": "Export with pandoc", + "Select output format": "Select output format", + "You have no rights to edit this note": "У вас нет прав редактировать эту заметку" } \ No newline at end of file diff --git a/locales/uk.json b/locales/uk.json index 0afc8796b5..5b99129538 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -100,5 +100,6 @@ "Select From Available Snippets": "Виберіть з доступних фрагментів коду", "OR": "АБО", "Export to Snippet": "Експорт фрагменту коду", - "Select Visibility Level": "Вибрати рівень видимості" + "Select Visibility Level": "Вибрати рівень видимості", + "You have no rights to edit this note": "Ви не маєте права редагувати цю нотатку" } diff --git a/public/js/index.js b/public/js/index.js index c736cb7f2c..8b86af65cb 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -256,6 +256,7 @@ const statusType = { // global vars window.loaded = false +let blockSourceView = false let needRefresh = false let isDirty = false let editShown = false @@ -290,6 +291,7 @@ const lastInfo = { } let personalInfo = {} let onlineUsers = [] +let currentPermission = '' const fileTypes = { pl: 'perl', cgi: 'perl', @@ -423,6 +425,8 @@ Visibility.change(function (e, state) { // when page ready $(document).ready(function () { + if (ui.toolbar.edit.data('blockSource')) { replaceUrlToViewMode(window.location.href) } + idle.checkAway() checkResponsive() // if in smaller screen, we don't need advanced scrollbar @@ -470,12 +474,14 @@ $(document).ready(function () { // allow on all tags key.filter = function (e) { return true } key('ctrl+alt+e', function (e) { + if (blockSourceView) return changeMode(modeType.edit) }) key('ctrl+alt+v', function (e) { changeMode(modeType.view) }) key('ctrl+alt+b', function (e) { + if (blockSourceView) return changeMode(modeType.both) }) // toggle-dropdown @@ -499,6 +505,72 @@ $(window).on('error', function () { // setNeedRefresh(); }) +function checkParameter (isLogin, permission) { + if (typeof isLogin !== 'boolean' || !permission) { + throw new Error('one or more parameter is incorrect') + } + return allowVisibleSource(isLogin, permission) +} + +function replaceUrlToViewMode (url) { + const urlHasEditOrBoth = /\?edit|\?both/ + if (urlHasEditOrBoth.test(url)) { + const newUrl = url.toString().replace(urlHasEditOrBoth, '?view') + window.location.replace(newUrl) + } +} + +function allowVisibleSource (isLogin, permission) { + switch (permission) { + case 'freely': + blockSourceView = false + break + case 'editable': + case 'limited': + if (!isLogin) { + blockSourceView = true + disableModeChangeControls() + } else { + blockSourceView = false + enableModeChangeControls() + } + break + case 'locked': + case 'protected': + case 'private': + if (personalInfo.userid && window.owner && personalInfo.userid === window.owner) { + blockSourceView = false + } else { + blockSourceView = true + disableModeChangeControls() + } + break + } +} + +function disableModeChangeControls () { + ui.toolbar.edit.attr({ + disabled: 'true' + }) + ui.toolbar.both.attr({ + disabled: 'true' + }) +} + +function enableModeChangeControls () { + ui.toolbar.edit.removeAttr('disabled') + ui.toolbar.both.removeAttr('disabled') +} + +function userIsLogin (userPersonalInfo) { + return !!userPersonalInfo && !!userPersonalInfo.login +} + +function isAllowUserChangeMode () { + const isOwner = personalInfo.userid && window.owner && personalInfo.userid === window.owner + return isOwner || !blockSourceView +} + setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown, editor) function autoSyncscroll () { @@ -1567,11 +1639,11 @@ function importFromUrl (url) { // mode ui.toolbar.mode.click(function () { - toggleMode() + if (isAllowUserChangeMode()) { return toggleMode() } }) // edit ui.toolbar.edit.click(function () { - changeMode(modeType.edit) + if (isAllowUserChangeMode()) { return changeMode(modeType.edit) } }) // view ui.toolbar.view.click(function () { @@ -1579,7 +1651,7 @@ ui.toolbar.view.click(function () { }) // both ui.toolbar.both.click(function () { - changeMode(modeType.both) + if (isAllowUserChangeMode()) { return changeMode(modeType.both) } }) ui.toolbar.night.click(function () { @@ -2047,6 +2119,16 @@ socket.on('refresh', function (data) { editor.setOption('maxLength', editorInstance.config.docmaxlength) updateInfo(data) updatePermission(data.permission) + currentPermission = data.permission + // run allowVisibleSource functionality + if (ui.toolbar.edit.data('blockSource')) { + try { + checkParameter(userIsLogin(personalInfo), currentPermission) + } catch (error) { + console.log(error) + } + } + if (ui.toolbar.edit.data('blockSource') && blockSourceView) { replaceUrlToViewMode(window.location.href) } if (!window.loaded) { // auto change mode if no content detected var nocontent = editor.getValue().length <= 0 diff --git a/public/views/codimd/header.ejs b/public/views/codimd/header.ejs index 86c006f783..3ecbf98334 100644 --- a/public/views/codimd/header.ejs +++ b/public/views/codimd/header.ejs @@ -76,20 +76,35 @@
  • Help
  • - + <% if(!allowVisibleSource) { %> + + <% } else { %> + + + + <% } %>