diff --git a/.gitignore b/.gitignore index 139fab33c..fc1136152 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ data npm-debug.log pins/ blob/ +blobstage/ privileged.conf diff --git a/.travis.yml b/.travis.yml index 4160b8719..09967f1f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ language: node_js env: matrix: - - "BROWSER='firefox:19:Windows 2012'" - - "BROWSER='chrome::Windows 2008'" + - "BROWSER='firefox::Windows 10'" + - "BROWSER='chrome::Windows 10'" + #- "BROWSER='MicrosoftEdge:14.14393:Windows 10'" + #- "BROWSER='internet explorer:11.103:Windows 10'" + #- "BROWSER='safari:10.0:macOS 10.12'" + #- "BROWSER='safari:9.0:OS X 10.11'" branches: only: - master diff --git a/TestSelenium.js b/TestSelenium.js index 9623577b5..fccd4e067 100644 --- a/TestSelenium.js +++ b/TestSelenium.js @@ -1,11 +1,13 @@ /* global process */ var WebDriver = require("selenium-webdriver"); +var nThen = require('nthen'); if (process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false') { // We can't do saucelabs on pull requests so don't fail. return; } +// https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/ var driver; if (process.env.SAUCE_USERNAME !== undefined) { var browserArray = process.env.BROWSER.split(':'); @@ -21,18 +23,59 @@ if (process.env.SAUCE_USERNAME !== undefined) { driver = new WebDriver.Builder().withCapabilities({ browserName: "chrome" }).build(); } -driver.get('http://localhost:3000/assert/'); -var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000); -report.getAttribute("class").then(function (cls) { - report.getText().then(function (text) { - console.log("\n-----\n" + text + "\n-----"); - driver.quit(); - if (!cls) { - throw new Error("cls is null"); - } else if (cls.indexOf("failure") !== -1) { - throw new Error("cls contains the word failure"); - } else if (cls.indexOf("success") === -1) { - throw new Error("cls does not contain the word success"); - } - }); +var SC_GET_DATA = "return (window.__CRYPTPAD_TEST__) ? window.__CRYPTPAD_TEST__.getData() : '[]'"; + +var failed = false; +var nt = nThen; +[ + //'/register/#?test=test', + '/assert/#?test=test', + // '/auth/#?test=test' // TODO(cjd): Not working on automatic tests, understand why. +].forEach(function (path) { + if (failed) { return; } + var url = 'http://localhost:3000' + path; + nt = nt(function (waitFor) { + var done = waitFor(); + console.log('\n\n-----TEST ' + url + ' -----'); + var waitTo = setTimeout(function () { + console.log("no report in 20 seconds, timing out"); + failed = true; + done(); + done = undefined; + }, 20000); + var logMore = function () { + if (!done) { return; } + driver.executeScript(SC_GET_DATA).then(waitFor(function (dataS) { + if (!done) { return; } + var data = JSON.parse(dataS); + data.forEach(function (d) { + if (d.type !== 'log') { return; } + console.log('>' + d.val); + }); + data.forEach(function (d) { + if (d.type !== 'report') { return; } + console.log('RESULT: ' + d.val); + if (d.val !== 'passed') { + if (d.error) { + console.log(d.error.message); + console.log(d.error.stack); + } + failed = true; + } + clearTimeout(waitTo); + console.log('-----END TEST ' + url + ' -----'); + done(); + done = undefined; + }); + if (done) { setTimeout(logMore, 50); } + })); + }; + driver.get(url).then(waitFor(logMore)); + }).nThen; +}); + +nt(function (waitFor) { + driver.quit().then(waitFor(function () { + if (failed) { process.exit(100); } + })); }); diff --git a/bower.json b/bower.json index 5734be6ec..0c80203da 100644 --- a/bower.json +++ b/bower.json @@ -37,6 +37,7 @@ "diff-dom": "^2.1.1", "alertifyjs": "^1.0.11", "scrypt-async": "^1.2.0", - "bootstrap": "#v4.0.0-alpha.6" + "bootstrap": "#v4.0.0-alpha.6", + "pdfjs-dist": "^1.8.398" } } diff --git a/config.example.js b/config.example.js index fe3f2fb91..c7a831b4c 100644 --- a/config.example.js +++ b/config.example.js @@ -10,7 +10,7 @@ module.exports = { // the port on which your httpd will listen - /* Cryptpad can be configured to send customized HTTP Headers + /* CryptPad can be configured to send customized HTTP Headers * These settings may vary widely depending on your needs * Examples are provided below */ @@ -31,18 +31,23 @@ module.exports = { * connect-src is used to restrict what domains can connect to the websocket. * * it is recommended that you configure these fields to match the - * domain which will serve your cryptpad instance. + * domain which will serve your CryptPad instance. */ "child-src 'self' *", + "media-src *", + /* this allows connections over secure or insecure websockets if you are deploying to production, you'll probably want to remove the ws://* directive, and change '*' to your domain */ - "connect-src 'self' ws: wss:", + "connect-src 'self' ws: wss: blob:", // data: is used by codemirror "img-src 'self' data: blob:", + + // for accounts.cryptpad.fr authentication + "frame-ancestors 'self' accounts.cryptpad.fr", ].join('; '), // CKEditor requires significantly more lax content security policy in order to function. @@ -82,24 +87,24 @@ module.exports = { */ //websocketPort: 3000, - /* if you want to run a different version of cryptpad but using the same websocket + /* if you want to run a different version of CryptPad but using the same websocket * server, you should use the other server port as websocketPort and disable * the websockets on that server */ //useExternalWebsocket: false, - /* If Cryptpad is proxied without using https, the server needs to know. + /* If CryptPad is proxied without using https, the server needs to know. * Specify 'useSecureWebsockets: true' so that it can send * Content Security Policy Headers that prevent http and https from mixing */ useSecureWebsockets: false, - /* Cryptpad can log activity to stdout + /* CryptPad can log activity to stdout * This may be useful for debugging */ logToStdout: false, - /* Cryptpad supports verbose logging + /* CryptPad supports verbose logging * (false by default) */ verbose: false, @@ -116,11 +121,57 @@ module.exports = { 'contact', ], - /* Domain - * If you want to have enable payments on your CryptPad instance, it has to be able to tell - * our account server what is your domain + /* Limits, Donations, Subscriptions and Contact + * + * By default, CryptPad limits every registered user to 50MB of storage. It also shows a + * donate button which allows for making a donation to support CryptPad development. + * + * You can either: + * A: Leave it exactly as it is. + * B: Hide the donate button. + * C: Change the donate button to a subscribe button, people who subscribe will get more + * storage on your instance and you get 50% of the revenue earned. + * + * CryptPad is developed by people who need to live and who deserve an equivilent life to + * what they would get at a company which monitizes user data. However, we intend to have + * a mutually positive relationship with every one of our users, including you. If you are + * getting value from CryptPad, you should be giving equal value back. + * + * If you are using CryptPad in a business context, please consider taking a support contract + * by contacting sales@cryptpad.fr + * + * If you choose A then there's nothing to do. + * + * If you choose B, set this variable to true and it will remove the donate button. + */ + removeDonateButton: false, + /* + * If you choose C, set allowSubscriptions to true, then set myDomain to the domain which people + * use to reach your CryptPad instance. Then contact sales@cryptpad.fr and tell us your domain. + * We will tell you what is needed to get paid. + */ + allowSubscriptions: false, + myDomain: 'i.did.not.read.my.config.myserver.tld', + + /* + * If you are using CryptPad internally and you want to increase the per-user storage limit, + * change the following value. + * + * Please note: This limit is what makes people subscribe and what pays for CryptPad + * development. Running a public instance that provides a "better deal" than cryptpad.fr + * is effectively using the project against itself. + */ + defaultStorageLimit: 50 * 1024 * 1024, + + /* + * By default, CryptPad also contacts our accounts server once a day to check for changes in + * the people who have accounts. This check-in will also send the version of your CryptPad + * instance and your email so we can reach you if we are aware of a serious problem. We will + * never sell it or send you marketing mail. If you want to block this check-in and remain + * completely invisible, set this and allowSubscriptions both to false. */ - // domain: 'https://cryptpad.fr', + adminEmail: 'i.did.not.read.my.config@cryptpad.fr', + /* You have the option of specifying an alternative storage adaptor. @@ -141,7 +192,7 @@ module.exports = { storage: './storage/file', /* - Cryptpad stores each document in an individual file on your hard drive. + CryptPad stores each document in an individual file on your hard drive. Specify a directory where files should be stored. It will be created automatically if it does not already exist. */ @@ -164,17 +215,17 @@ module.exports = { */ blobStagingPath: './blobstage', - /* Cryptpad's file storage adaptor closes unused files after a configurale + /* CryptPad's file storage adaptor closes unused files after a configurale * number of milliseconds (default 30000 (30 seconds)) */ channelExpirationMs: 30000, - /* Cryptpad's file storage adaptor is limited by the number of open files. + /* CryptPad's file storage adaptor is limited by the number of open files. * When the adaptor reaches openFileLimit, it will clean up older files */ openFileLimit: 2048, - /* Cryptpad's socket server can be extended to respond to RPC calls + /* CryptPad's socket server can be extended to respond to RPC calls * you can configure it to respond to custom RPC calls if you like. * provide the path to your RPC module here, or `false` if you would * like to disable the RPC interface completely @@ -211,12 +262,6 @@ module.exports = { */ //restrictUploads: false, - /* Default user storage limit (bytes) - * if you don't want to limit users, - * you can set this to the size of your hard disk - */ - defaultStorageLimit: 50 * 1024 * 1024, - /* Max Upload Size (bytes) * this sets the maximum size of any one file uploaded to the server. * anything larger than this size will be rejected @@ -232,7 +277,7 @@ module.exports = { */ //logFeedback: true, - /* it is recommended that you serve cryptpad over https + /* it is recommended that you serve CryptPad over https * the filepaths below are used to configure your certificates */ //privKeyAndCertFiles: [ diff --git a/customize.dist/BottomBar.html b/customize.dist/BottomBar.html deleted file mode 100644 index c8d43dfce..000000000 --- a/customize.dist/BottomBar.html +++ /dev/null @@ -1,16 +0,0 @@ - -
-
-
- - - -

-

-
-
-

-

-
-
-
diff --git a/customize.dist/about.html b/customize.dist/about.html index 954a3fb6f..dadd21e3b 100644 --- a/customize.dist/about.html +++ b/customize.dist/about.html @@ -39,6 +39,9 @@ Blog + + + @@ -114,7 +117,7 @@ - + diff --git a/customize.dist/application_config.js b/customize.dist/application_config.js index 24ed2c740..af2474775 100644 --- a/customize.dist/application_config.js +++ b/customize.dist/application_config.js @@ -4,7 +4,8 @@ define(function() { /* Select the buttons displayed on the main page to create new collaborative sessions * Existing types : pad, code, poll, slide */ - config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard']; + config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file']; + config.registeredOnlyTypes = ['file']; /* Cryptpad apps use a common API to display notifications to users * by default, notifications are hidden after 5 seconds @@ -37,8 +38,6 @@ define(function() { config.enableHistory = true; - config.enablePinLimit = true; - /* user passwords are hashed with scrypt, and salted with their username. this value will be appended to the username, causing the resulting hash to differ from other CryptPad instances if customized. This makes it diff --git a/customize.dist/bg.jpg b/customize.dist/bg.jpg deleted file mode 100644 index fa5214591..000000000 Binary files a/customize.dist/bg.jpg and /dev/null differ diff --git a/customize.dist/bg2.jpg b/customize.dist/bg2.jpg deleted file mode 100644 index a7598eee1..000000000 Binary files a/customize.dist/bg2.jpg and /dev/null differ diff --git a/customize.dist/contact.html b/customize.dist/contact.html index 5d43d15a5..768cf2f7c 100644 --- a/customize.dist/contact.html +++ b/customize.dist/contact.html @@ -39,6 +39,9 @@ Blog + + + @@ -111,7 +114,7 @@ - + diff --git a/customize.dist/fr.png b/customize.dist/fr.png deleted file mode 100644 index 8332c4ec2..000000000 Binary files a/customize.dist/fr.png and /dev/null differ diff --git a/customize.dist/header.js b/customize.dist/header.js new file mode 100644 index 000000000..7a2ac9295 --- /dev/null +++ b/customize.dist/header.js @@ -0,0 +1,53 @@ +define([ + 'jquery', + '/customize/application_config.js', + '/common/cryptpad-common.js', + '/api/config', +], function ($, Config, Cryptpad, ApiConfig) { + + window.APP = { + Cryptpad: Cryptpad, + }; + + var Messages = Cryptpad.Messages; + + $(function () { + // Language selector + var $sel = $('#language-selector'); + Cryptpad.createLanguageSelector(undefined, $sel); + $sel.find('button').addClass('btn').addClass('btn-secondary'); + $sel.show(); + + var $upgrade = $('#upgrade'); + + var showUpgrade = function (text, feedback, url) { + if (ApiConfig.removeDonateButton) { return; } + if (localStorage.plan) { return; } + if (!text) { return; } + $upgrade.text(text).show(); + $upgrade.click(function () { + Cryptpad.feedback(feedback); + window.open(url,'_blank'); + }); + }; + + // User admin menu + var $userMenu = $('#user-menu'); + var userMenuCfg = { + $initBlock: $userMenu + }; + var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg); + $userAdmin.find('button').addClass('btn').addClass('btn-secondary'); + + $(window).click(function () { + $('.cryptpad-dropdown').hide(); + }); + + if (Cryptpad.isLoggedIn() && ApiConfig.allowSubscriptions) { + showUpgrade(Messages.upgradeAccount, "HOME_UPGRADE_ACCOUNT", Cryptpad.upgradeURL); + } else { + showUpgrade(Messages.supportCryptpad, "HOME_SUPPORT_CRYPTPAD", Cryptpad.donateURL); + } + }); +}); + diff --git a/customize.dist/heart.png b/customize.dist/heart.png deleted file mode 100644 index d9ee53e59..000000000 Binary files a/customize.dist/heart.png and /dev/null differ diff --git a/customize.dist/index.html b/customize.dist/index.html index a1b1fe56b..db9cef793 100644 --- a/customize.dist/index.html +++ b/customize.dist/index.html @@ -39,6 +39,9 @@ Blog + + + @@ -233,7 +236,7 @@ - + diff --git a/customize.dist/logo-xwiki.png b/customize.dist/logo-xwiki.png deleted file mode 100644 index 375739d2b..000000000 Binary files a/customize.dist/logo-xwiki.png and /dev/null differ diff --git a/customize.dist/logo-xwiki2.png b/customize.dist/logo-xwiki2.png deleted file mode 100644 index 047c89ea7..000000000 Binary files a/customize.dist/logo-xwiki2.png and /dev/null differ diff --git a/customize.dist/main.css b/customize.dist/main.css index 063324504..49f06eedc 100644 --- a/customize.dist/main.css +++ b/customize.dist/main.css @@ -387,6 +387,8 @@ left: 0; right: 0; text-align: center; + transition: opacity 750ms; + transition-delay: 3000ms; } @media screen and (max-height: 600px) { .cp #loadingTip { @@ -525,6 +527,22 @@ margin: 0px 10px; line-height: 40px; } +#cryptpadTopBar .right .buttonSuccess { + color: #fff; + background: #5cb85c; + border-color: #5cb85c; +} +#cryptpadTopBar .right .buttonSuccess:hover { + color: #fff; + background: #449d44; + border: 1px solid #419641; +} +#cryptpadTopBar .right .buttonSuccess span { + color: #fff; +} +#cryptpadTopBar .right .buttonSuccess .large { + margin-left: 5px; +} #cryptpadTopBar .right button .buttonTitle .fa-user { margin-right: 5px; } @@ -875,6 +893,12 @@ html.cp, .cp #main_other #main-container { display: inline-block; } +.cp #main #userForm .extra p, +.cp #main_other #userForm .extra p { + font-size: 28px; + padding: 15px; + text-align: center; +} .cp #main #data, .cp #main_other #data { width: 600px; diff --git a/customize.dist/main.js b/customize.dist/main.js index 088e1f51a..f2580bbe1 100644 --- a/customize.dist/main.js +++ b/customize.dist/main.js @@ -1,7 +1,8 @@ define([ 'jquery', '/customize/application_config.js', - '/common/cryptpad-common.js' + '/common/cryptpad-common.js', + '/customize/header.js', ], function ($, Config, Cryptpad) { window.APP = { @@ -13,25 +14,10 @@ define([ $(function () { var $main = $('#mainBlock'); - // Language selector - var $sel = $('#language-selector'); - Cryptpad.createLanguageSelector(undefined, $sel); - $sel.find('button').addClass('btn').addClass('btn-secondary'); - $sel.show(); - - // User admin menu - var $userMenu = $('#user-menu'); - var userMenuCfg = { - $initBlock: $userMenu - }; - var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg); - $userAdmin.find('button').addClass('btn').addClass('btn-secondary'); - $(window).click(function () { $('.cryptpad-dropdown').hide(); }); - // main block is hidden in case javascript is disabled $main.removeClass('hidden'); @@ -58,8 +44,8 @@ define([ }); $loggedInBlock.removeClass('hidden'); - //return; - } else { + } + else { $main.find('#userForm').removeClass('hidden'); $('#name').focus(); } @@ -70,6 +56,8 @@ define([ var $container = $('
', {'class': 'dropdown-bar'}).appendTo($parent); Config.availablePadTypes.forEach(function (el) { if (el === 'drive') { return; } + if (!Cryptpad.isLoggedIn() && Config.registeredOnlyTypes && + Config.registeredOnlyTypes.indexOf(el) !== -1) { return; } options.push({ tag: 'a', attributes: { @@ -90,7 +78,6 @@ define([ $block.appendTo($parent); }; - /* Log in UI */ var Login; // deferred execution to avoid unnecessary asset loading diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 2dac5b6c9..67759a15e 100644 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -9,6 +9,7 @@ var map = { 'de': 'Deutsch', 'pt-br': 'Português do Brasil', 'ro': 'Română', + 'zh': '繁體中文', }; var getStoredLanguage = function () { return localStorage.getItem(LS_LANG); }; diff --git a/customize.dist/openpaas.png b/customize.dist/openpaas.png deleted file mode 100644 index aa91bfe98..000000000 Binary files a/customize.dist/openpaas.png and /dev/null differ diff --git a/customize.dist/openpaasng.png b/customize.dist/openpaasng.png deleted file mode 100644 index 54168332c..000000000 Binary files a/customize.dist/openpaasng.png and /dev/null differ diff --git a/customize.dist/privacy.html b/customize.dist/privacy.html index 35bab1958..8218dbad2 100644 --- a/customize.dist/privacy.html +++ b/customize.dist/privacy.html @@ -39,6 +39,9 @@ Blog + + +
@@ -132,7 +135,7 @@ - + diff --git a/customize.dist/src/fragments/footer.html b/customize.dist/src/fragments/footer.html index 55b332a0f..a43182135 100644 --- a/customize.dist/src/fragments/footer.html +++ b/customize.dist/src/fragments/footer.html @@ -39,5 +39,5 @@ - + diff --git a/customize.dist/src/fragments/topbar.html b/customize.dist/src/fragments/topbar.html index a6b459b35..c1ed7e24b 100644 --- a/customize.dist/src/fragments/topbar.html +++ b/customize.dist/src/fragments/topbar.html @@ -24,4 +24,7 @@ Blog + + + diff --git a/customize.dist/src/less/cryptpad.less b/customize.dist/src/less/cryptpad.less index 0ee8785e6..49608df00 100644 --- a/customize.dist/src/less/cryptpad.less +++ b/customize.dist/src/less/cryptpad.less @@ -337,6 +337,14 @@ noscript { display: inline-block; } + #userForm .extra { + p { + font-size: 28px; + padding: 15px; + text-align: center; + } + } + #data { p { margin: 0; diff --git a/customize.dist/src/less/loading.less b/customize.dist/src/less/loading.less index 6dcf2491a..cc23c49d9 100644 --- a/customize.dist/src/less/loading.less +++ b/customize.dist/src/less/loading.less @@ -36,6 +36,9 @@ left: 0; right: 0; text-align: center; + + transition: opacity 750ms; + transition-delay: 3000ms; @media screen and (max-height: @media-medium-screen) { display: none; } diff --git a/customize.dist/src/less/topbar.less b/customize.dist/src/less/topbar.less index b10309f61..a394c4eeb 100644 --- a/customize.dist/src/less/topbar.less +++ b/customize.dist/src/less/topbar.less @@ -47,6 +47,24 @@ margin: 0px 10px; line-height: 40px; + .buttonSuccess { + // Bootstrap 4 colors + color: #fff; + background: @toolbar-green; + border-color: @toolbar-green; + &:hover { + color: #fff; + background: #449d44; + border: 1px solid #419641; + } + span { + color: #fff; + } + .large { + margin-left: 5px; + } + } + button { .buttonTitle { .fa-user { diff --git a/customize.dist/terms.html b/customize.dist/terms.html index 2504b1147..bda3dcbb9 100644 --- a/customize.dist/terms.html +++ b/customize.dist/terms.html @@ -39,6 +39,9 @@ Blog + + + @@ -115,7 +118,7 @@ - + diff --git a/customize.dist/translations/messages.es.js b/customize.dist/translations/messages.es.js index 40efab91e..a128c2e39 100644 --- a/customize.dist/translations/messages.es.js +++ b/customize.dist/translations/messages.es.js @@ -13,8 +13,7 @@ define(function () { out.type.slide = 'Presentación'; out.type.whiteboard = 'Pizarra'; - out.updated_0_common_connectionLost = "Connexión perdida
El documento está ahora en modo solo lectura hasta que la conexión vuelva."; - out.common_connectionLost = out.updated_0_common_connectionLost; + out.common_connectionLost = "Connexión perdida
El documento está ahora en modo solo lectura hasta que la conexión vuelva."; out.disconnected = "Desconectado"; out.synchronizing = "Sincronización"; @@ -200,7 +199,6 @@ define(function () { out.fm_info_root = "Crea carpetas aquí para organizar tus documentos."; out.fm_info_unsorted = "Contiene todos los documentos que has visitado que no estan organizados en \"Documentos\" o movidos a la \"Papelera\"."; out.fm_info_template = "Contiene todas las plantillas que puedes volver a usar para crear nuevos documentos."; - out.fm_info_trash = "Archivos eliminados de la papelera también se eliminan de \"Todos los archivos\" y es imposible recuparlos desde el explorador."; out.fm_info_allFiles = "Contiene todos los archivos de \"Documentos\", \"Sin organizar\" y \"Papelera\". No puedes mover o eliminar archivos aquí."; out.fm_alert_backupUrl = "Enlace de copia de seguridad para este drive. Te recomendamos muy fuertemente que lo guardes secreto.
Lo puedes usar para recuparar todos tus archivos en el caso que la memoria de tu navegador se borre.
Cualquiera con este enlace puede editar o eliminar todos los archivos en el explorador.
"; out.fm_backup_title = "Enlace de copia de seguridad"; @@ -409,6 +407,8 @@ define(function () { out.deleted = "El pad fue borrado de tu CryptDrive"; out.upgrade = "Mejorar"; out.upgradeTitle = "Mejora tu cuenta para obtener más espacio"; + out.upgradeAccount = "Mejorar cuenta"; + out.MB = "MB"; out.GB = "GB"; out.KB = "KB"; @@ -417,7 +417,6 @@ define(function () { out.formattedKB = "{0} KB"; out.pinLimitReached = "Has llegado al limite de espacio"; - out.pinLimitReachedAlert = "Has llegado al limite de espacio. Nuevos pads no serán movidos a tu CryptDrive.
Para resolver este problema, puedes quitar pads de tu CryptDrive (incluso en la papelera) o mejorar tu cuenta para obtener más espacio."; out.pinLimitNotPinned = "Has llegado al limite de espacio.
Este pad no estará presente en tu CryptDrive."; out.pinLimitDrive = "Has llegado al limite de espacio.
No puedes crear nuevos pads."; out.printTransition = "Activar transiciones"; @@ -429,5 +428,34 @@ define(function () { out.upload_uploadPending = "Ya tienes una subida en progreso. ¿Cancelar y subir el nuevo archivo?"; out.upload_success = "Tu archivo ({0}) ha sido subido con éxito y fue añadido a tu drive."; + // 1.7.0 - Hodag + out.comingSoon = "Próximamente..."; // "Coming soon..." + out.newVersion = ["CryptPad ha sido actualizado!", + "Puedes ver lo que ha cambiada aquí (en inglés):", + "Notas de versión para CryptPad {0}"].join("
"); + out.pinLimitReachedAlert = ["Has llegado a tu limite de espacio. Nuevos pads no serán guardados en tu CryptDrive.", + "Puedes eliminar pads de tu CryptDrive o suscribirte a una oferta premium para obtener más espacio."].join("
"); + out.pinLimitReachedAlertNoAccounts = "Has llegado a tu limite de espacio"; + out.previewButtonTitle = "Mostrar/esconder la vista previa Markdown"; + out.fm_info_trash = "Vacía tu papelera para liberar espaci en tu CryptDrive."; + out.fm_info_anonymous = "No estás conectado, así que estos pads pueden ser borrados (¿por qué?). Registrate o Inicia sesión para asegurarlos."; + out.fm_alert_anonymous = "Hola, estás usando CryptPad anónimamente. Está bien, pero tus pads pueden ser borrados después de un périodo de inactividad. Hemos desactivado funciones avanzadas de CryptDrive para usuarios anónimos porque queremos ser claros que no es un lugar seguro para almacenar cosas. Puedes leer este articulo (en inglés) sobre por qué hacemos esto y por qué deberías Registrarte e Iniciar sesión."; + out.fm_error_cantPin = "Error del servidor. Por favor, recarga la página e intentalo de nuevo."; + out.upload_notEnoughSpace = "No tienes suficiente espacio para este archivo en tu CryptDrive"; + out.upload_tooLarge = "Este archivo supera el límite de carga."; + out.upload_choose = "Escoge un archivo"; + out.upload_pending = "Esperando"; + out.upload_cancelled = "Cancelado"; + out.upload_name = "Nombre"; + out.upload_size = "Tamaño"; + out.upload_progress = "Progreso"; + out.download_button = "Descifrar y descargar"; + out.warn_notPinned = "Este pad no está en ningun CryptDrive. Expirará después de 3 meses. Acerca de..."; + + out.poll_remove = "Quitar"; + out.poll_edit = "Editar"; + out.poll_locked = "Cerrado"; + out.poll_unlocked = "Abierto"; + return out; }); diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index a112d397e..64b40f553 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -60,10 +60,14 @@ define(function () { out.upgrade = "Augmenter votre limite"; out.upgradeTitle = "Améliorer votre compte pour augmenter la limite de stockage"; + + out.upgradeAccount = "Améliorer le compte"; out.MB = "Mo"; out.GB = "Go"; out.KB = "Ko"; + out.supportCryptpad = "Soutenir CryptPad"; + out.formattedMB = "{0} Mo"; out.formattedGB = "{0} Go"; out.formattedKB = "{0} Ko"; @@ -76,7 +80,7 @@ define(function () { out.updated_0_pinLimitReachedAlert = "Vous avez atteint votre limite de stockage. Les nouveaux pads ne seront pas enregistrés dans votre CryptDrive.
" + 'Vous pouvez soit supprimer des pads de votre CryptDrive, soit vous abonner à une offre premium pour augmenter la limite maximale.'; out.pinLimitReachedAlert = out.updated_0_pinLimitReachedAlert; - out.pinAboveLimitAlert = 'Depuis la dernière version, nous imposons désormais une limite de 50 Mo de stockage gratuit et vous utilisez actuellement {0}. You devriez soit supprimer certains pads ou soit vous abonner sur accounts.cryptpad.fr. Votre contribution nous aidera à améliorer CryptPad et à répandre le Zero Knowledge. Vous pouvez contacter le support pour tout problème ou question concernant ces changements.'; + out.pinLimitReachedAlertNoAccounts = out.pinLimitReached; out.pinLimitNotPinned = "Vous avez atteint votre limite de stockage.
"+ "Ce pad n'est pas enregistré dans votre CryptDrive."; out.pinLimitDrive = out.pinLimitReached+ ".
" + @@ -193,6 +197,11 @@ define(function () { out.poll_titleHint = "Titre"; out.poll_descriptionHint = "Description"; + out.poll_remove = "Supprimer"; + out.poll_edit = "Modifier"; + out.poll_locked = "Verrouillé"; + out.poll_unlocked = "Déverrouillé"; + // Canvas out.canvas_clear = "Nettoyer"; out.canvas_delete = "Supprimer la sélection"; @@ -299,6 +308,8 @@ define(function () { out.login_invalPass = 'Mot de passe requis'; out.login_unhandledError = "Une erreur inattendue s'est produite :("; + out.login_notRegistered = 'Pas encore inscrit ?'; + out.register_importRecent = "Importer l'historique (Recommendé)"; out.register_acceptTerms = "J'accepte les conditions d'utilisation"; out.register_passwordsDontMatch = "Les mots de passe doivent être identiques!"; @@ -369,6 +380,7 @@ define(function () { out.upload_name = "Nom du fichier"; out.upload_size = "Taille"; out.upload_progress = "État"; + out.upload_mustLogin = "Vous devez vous connecter pour uploader un fichier"; out.download_button = "Déchiffrer et télécharger"; // general warnings diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 751cf3633..fa08fb1c7 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -62,10 +62,14 @@ define(function () { out.upgrade = "Upgrade"; out.upgradeTitle = "Upgrade your account to increase the storage limit"; + + out.upgradeAccount = "Upgrade account"; out.MB = "MB"; out.GB = "GB"; out.KB = "KB"; + out.supportCryptpad = "Support CryptPad"; + out.formattedMB = "{0} MB"; out.formattedGB = "{0} GB"; out.formattedKB = "{0} KB"; @@ -78,7 +82,7 @@ define(function () { out.updated_0_pinLimitReachedAlert = "You've reached your storage limit. New pads won't be stored in your CryptDrive.
" + 'You can either remove pads from your CryptDrive or subscribe to a premium offer to increase your limit.'; out.pinLimitReachedAlert = out.updated_0_pinLimitReachedAlert; - out.pinAboveLimitAlert = 'As of this release, we are imposing a 50MB limit on free data storage and you are currently using {0}. You will need to either delete some pads or subscribe on accounts.cryptpad.fr. Your contribution will help us improve CryptPad and spread Zero Knowledge. Please contact support if you have any other questions.'; + out.pinLimitReachedAlertNoAccounts = out.pinLimitReached; out.pinLimitNotPinned = "You've reached your storage limit.
"+ "This pad is not stored in your CryptDrive."; out.pinLimitDrive = "You've reached your storage limit.
" + @@ -195,6 +199,11 @@ define(function () { out.poll_titleHint = "Title"; out.poll_descriptionHint = "Describe your poll, and use the 'publish' button when you're done. Anyone with the link can change the description, but this is discouraged."; + out.poll_remove = "Remove"; + out.poll_edit = "Edit"; + out.poll_locked = "Locked"; + out.poll_unlocked = "Unlocked"; + // Canvas out.canvas_clear = "Clear"; out.canvas_delete = "Delete selection"; @@ -301,6 +310,8 @@ define(function () { out.login_invalPass = 'Password required'; out.login_unhandledError = 'An unexpected error occurred :('; + out.login_notRegistered = 'Not registered?'; + out.register_importRecent = "Import pad history (Recommended)"; out.register_acceptTerms = "I accept the terms of service"; out.register_passwordsDontMatch = "Passwords do not match!"; @@ -374,6 +385,7 @@ define(function () { out.upload_name = "File name"; out.upload_size = "Size"; out.upload_progress = "Progress"; + out.upload_mustLogin = "You must be logged in to upload files"; out.download_button = "Decrypt & Download"; // general warnings @@ -395,6 +407,7 @@ define(function () { out.main_zeroKnowledge = 'Zero Knowledge'; out.main_zeroKnowledge_p = "You don't have to trust that we won't look at your pads, with CryptPad's revolutionary Zero Knowledge Technology we can't. Learn more about how we protect your Privacy and Security."; out.main_writeItDown = 'Write it down'; + out.main_writeItDown_p = "The greatest projects come from the smallest ideas. Take down the moments of inspiration and unexpected ideas because you never know which one might be a breakthrough."; out.main_share = 'Share the link, share the pad'; out.main_share_p = "Grow your ideas together: conduct efficient meetings, collaborate on TODO lists and make quick presentations with all your friends and all your devices."; diff --git a/customize.dist/translations/messages.ro.js b/customize.dist/translations/messages.ro.js index 4e2134c7c..75c1c5852 100644 --- a/customize.dist/translations/messages.ro.js +++ b/customize.dist/translations/messages.ro.js @@ -1,357 +1,371 @@ define(function () { var out = {}; - /* - * - * ro - * - */ + out.main_title = "CryptPad: Zero Knowledge, Colaborare în timp real"; + out.main_slogan = "Puterea stă în cooperare - Colaborarea este cheia"; - out.main_title = ""; // "CryptPad: Zero Knowledge, Collaborative Real Time Editing" - out.main_slogan = ""; // "Unity is Strength - Collaboration is Key" - out.type = ""; // {"pad":"Rich text","code":"Code","poll":"Poll","slide":"Presentation","drive":"Drive","whiteboard":"Whiteboard","file":"File","media":"Media"} - out.button_newpad = ""; // "New Rich Text pad" - out.button_newcode = ""; // "New Code pad" - out.button_newpoll = ""; // "New Poll" - out.button_newslide = ""; // "New Presentation" - out.button_newwhiteboard = ""; // "New Whiteboard" - out.updated_0_common_connectionLost = ""; // "Server Connection Lost
You're now in read-only mode until the connection is back." - out.common_connectionLost = out.updated_0_common_connectionLost; // TODO: Key updated --> make sure the updated key "out.updated_0_common_connectionLost" exists and is translated before that one. - out.websocketError = ""; // "Unable to connect to the websocket server..." - out.typeError = ""; // "This pad is not compatible with the selected application" - out.onLogout = ""; // "You are logged out, click here to log in
or press Escape to access your pad in read-only mode." - out.wrongApp = ""; // "Unable to display the content of that realtime session in your browser. Please try to reload that page." - out.loading = ""; // "Loading..." - out.error = ""; // "Error" - out.saved = ""; // "Saved" - out.synced = ""; // "Everything is saved" - out.deleted = ""; // "Pad deleted from your CryptDrive" - out.disconnected = ""; // "Disconnected" - out.synchronizing = ""; // "Synchronizing" - out.reconnecting = ""; // "Reconnecting..." - out.lag = ""; // "Lag" - out.readonly = ""; // "Read only" - out.anonymous = ""; // "Anonymous" - out.yourself = ""; // "Yourself" - out.anonymousUsers = ""; // "anonymous editors" - out.anonymousUser = ""; // "anonymous editor" - out.users = ""; // "Users" - out.and = ""; // "And" - out.viewer = ""; // "viewer" - out.viewers = ""; // "viewers" - out.editor = ""; // "editor" - out.editors = ""; // "editors" - out.language = ""; // "Language" - out.upgrade = ""; // "Upgrade" - out.upgradeTitle = ""; // "Upgrade your account to increase the storage limit" - out.MB = ""; // "MB" - out.greenLight = ""; // "Everything is working fine" - out.orangeLight = ""; // "Your slow connection may impact your experience" - out.redLight = ""; // "You are disconnected from the session" - out.pinLimitReached = ""; // "You've reached your storage limit" - out.pinLimitReachedAlert = ""; // "You've reached your storage limit. New pads won't be stored in your CryptDrive.
To fix this problem, you can either remove pads from your CryptDrive (including the trash) or subscribe to a premium offer to increase your limit." - out.pinLimitNotPinned = ""; // "You've reached your storage limit.
This pad is not stored in your CryptDrive." - out.pinLimitDrive = ""; // "You've reached your storage limit.
You can't create new pads." - out.importButtonTitle = ""; // "Import a pad from a local file" - out.exportButtonTitle = ""; // "Export this pad to a local file" - out.exportPrompt = ""; // "What would you like to name your file?" - out.changeNamePrompt = ""; // "Change your name (leave empty to be anonymous): " - out.user_rename = ""; // "Change display name" - out.user_displayName = ""; // "Display name" - out.user_accountName = ""; // "Account name" - out.clickToEdit = ""; // "Click to edit" - out.forgetButtonTitle = ""; // "Move this pad to the trash" - out.forgetPrompt = ""; // "Clicking OK will move this pad to your trash. Are you sure?" - out.movedToTrash = ""; // "That pad has been moved to the trash.
Access my Drive" - out.shareButton = ""; // "Share" - out.shareSuccess = ""; // "Copied link to clipboard" - out.newButton = ""; // "New" - out.newButtonTitle = ""; // "Create a new pad" - out.saveTemplateButton = ""; // "Save as template" - out.saveTemplatePrompt = ""; // "Choose a title for the template" - out.templateSaved = ""; // "Template saved!" - out.selectTemplate = ""; // "Select a template or press escape" - out.presentButtonTitle = ""; // "Enter presentation mode" - out.presentSuccess = ""; // "Hit ESC to exit presentation mode" - out.backgroundButtonTitle = ""; // "Change the background color in the presentation" - out.colorButtonTitle = ""; // "Change the text color in presentation mode" - out.printButton = ""; // "Print (enter)" - out.printButtonTitle = ""; // "Print your slides or export them as a PDF file" - out.printOptions = ""; // "Layout options" - out.printSlideNumber = ""; // "Display the slide number" - out.printDate = ""; // "Display the date" - out.printTitle = ""; // "Display the pad title" - out.printCSS = ""; // "Custom style rules (CSS):" - out.printTransition = ""; // "Enable transition animations" - out.slideOptionsTitle = ""; // "Customize your slides" - out.slideOptionsButton = ""; // "Save (enter)" - out.editShare = ""; // "Editing link" - out.editShareTitle = ""; // "Copy the editing link to clipboard" - out.editOpen = ""; // "Open editing link in a new tab" - out.editOpenTitle = ""; // "Open this pad in editing mode in a new tab" - out.viewShare = ""; // "Read-only link" - out.viewShareTitle = ""; // "Copy the read-only link to clipboard" - out.viewOpen = ""; // "Open read-only link in a new tab" - out.viewOpenTitle = ""; // "Open this pad in read-only mode in a new tab" - out.notifyJoined = ""; // "{0} has joined the collaborative session" - out.notifyRenamed = ""; // "{0} is now known as {1}" - out.notifyLeft = ""; // "{0} has left the collaborative session" - out.okButton = ""; // "OK (enter)" - out.cancel = ""; // "Cancel" - out.cancelButton = ""; // "Cancel (esc)" - out.historyButton = ""; // "Display the document history" - out.history_next = ""; // "Go to the next version" - out.history_prev = ""; // "Go to the previous version" - out.history_goTo = ""; // "Go to the selected version" - out.history_close = ""; // "Back" - out.history_closeTitle = ""; // "Close the history" - out.history_restore = ""; // "Restore" - out.history_restoreTitle = ""; // "Restore the selected version of the document" - out.history_restorePrompt = ""; // "Are you sure you want to replace the current version of the document by the displayed one?" - out.history_restoreDone = ""; // "Document restored" - out.history_version = ""; // "Version:" - out.poll_title = ""; // "Zero Knowledge Date Picker" - out.poll_subtitle = ""; // "Zero Knowledge, realtime scheduling" - out.poll_p_save = ""; // "Your settings are updated instantly, so you never need to save." - out.poll_p_encryption = ""; // "All your input is encrypted so only people who have the link can access it. Even the server cannot see what you change." - out.wizardLog = ""; // "Click the button in the top left to return to your poll" - out.wizardTitle = ""; // "Use the wizard to create your poll" - out.wizardConfirm = ""; // "Are you really ready to add these options to your poll?" - out.poll_publish_button = ""; // "Publish" - out.poll_admin_button = ""; // "Admin" - out.poll_create_user = ""; // "Add a new user" - out.poll_create_option = ""; // "Add a new option" - out.poll_commit = ""; // "Commit" - out.poll_closeWizardButton = ""; // "Close wizard" - out.poll_closeWizardButtonTitle = ""; // "Close wizard" - out.poll_wizardComputeButton = ""; // "Compute Options" - out.poll_wizardClearButton = ""; // "Clear Table" - out.poll_wizardDescription = ""; // "Automatically create a number of options by entering any number of dates and times segments" - out.poll_wizardAddDateButton = ""; // "+ Dates" - out.poll_wizardAddTimeButton = ""; // "+ Times" - out.poll_optionPlaceholder = ""; // "Option" - out.poll_userPlaceholder = ""; // "Your name" - out.poll_removeOption = ""; // "Are you sure you'd like to remove this option?" - out.poll_removeUser = ""; // "Are you sure you'd like to remove this user?" - out.poll_titleHint = ""; // "Title" - out.poll_descriptionHint = ""; // "Describe your poll, and use the 'publish' button when you're done. Anyone with the link can change the description, but this is discouraged." - out.canvas_clear = ""; // "Clear" - out.canvas_delete = ""; // "Delete selection" - out.canvas_disable = ""; // "Disable draw" - out.canvas_enable = ""; // "Enable draw" - out.canvas_width = ""; // "Width" - out.canvas_opacity = ""; // "Opacity" - out.fm_rootName = ""; // "Documents" - out.fm_trashName = ""; // "Trash" - out.fm_unsortedName = ""; // "Unsorted files" - out.fm_filesDataName = ""; // "All files" - out.fm_templateName = ""; // "Templates" - out.fm_searchName = ""; // "Search" - out.fm_searchPlaceholder = ""; // "Search..." - out.fm_newButton = ""; // "New" - out.fm_newButtonTitle = ""; // "Create a new pad or folder" - out.fm_newFolder = ""; // "New folder" - out.fm_newFile = ""; // "New pad" - out.fm_folder = ""; // "Folder" - out.fm_folderName = ""; // "Folder name" - out.fm_numberOfFolders = ""; // "# of folders" - out.fm_numberOfFiles = ""; // "# of files" - out.fm_fileName = ""; // "File name" - out.fm_title = ""; // "Title" - out.fm_type = ""; // "Type" - out.fm_lastAccess = ""; // "Last access" - out.fm_creation = ""; // "Creation" - out.fm_forbidden = ""; // "Forbidden action" - out.fm_originalPath = ""; // "Original path" - out.fm_openParent = ""; // "Show in folder" - out.fm_noname = ""; // "Untitled Document" - out.fm_emptyTrashDialog = ""; // "Are you sure you want to empty the trash?" - out.fm_removeSeveralPermanentlyDialog = ""; // "Are you sure you want to remove these {0} elements from the trash permanently?" - out.fm_removePermanentlyDialog = ""; // "Are you sure you want to remove that element permanently?" - out.fm_removeSeveralDialog = ""; // "Are you sure you want to move these {0} elements to the trash?" - out.fm_removeDialog = ""; // "Are you sure you want to move {0} to the trash?" - out.fm_restoreDialog = ""; // "Are you sure you want to restore {0} to its previous location?" - out.fm_unknownFolderError = ""; // "The selected or last visited directory no longer exist. Opening the parent folder..." - out.fm_contextMenuError = ""; // "Unable to open the context menu for that element. If the problem persist, try to reload the page." - out.fm_selectError = ""; // "Unable to select the targetted element. If the problem persist, try to reload the page." - out.fm_categoryError = ""; // "Unable to open the selected category, displaying root." - out.fm_info_root = ""; // "Create as many nested folders here as you want to sort your files." - out.fm_info_unsorted = ""; // "Contains all the files you've visited that are not yet sorted in \"Documents\" or moved to the \"Trash\"." - out.fm_info_template = ""; // "Contains all the pads stored as templates and that you can re-use when you create a new pad." - out.fm_info_trash = ""; // "Files deleted from the trash are also removed from \"All files\" and it is impossible to recover them from the file manager." - out.fm_info_allFiles = ""; // "Contains all the files from \"Documents\", \"Unsorted\" and \"Trash\". You can't move or remove files from here." - out.fm_info_login = ""; // "Log in" - out.fm_info_register = ""; // "Sign up" - out.fm_info_anonymous = ""; // "You are not logged in so these pads may be deleted (find out why). Sign up or Log in to keep them alive." - out.fm_alert_backupUrl = ""; // "Backup link for this drive.
It is highly recommended that you keep ip for yourself only.
You can use it to retrieve all your files in case your browser memory got erased.
Anybody with that link can edit or remove all the files in your file manager.
" - out.fm_alert_anonymous = ""; // "Hello there, you are currently using CryptPad anonymously, that's ok but your pads may be deleted after a period of inactivity. We have disabled advanced features of the drive for anonymous users because we want to be clear that it is not a safe place to store things. You can read more about why we are doing this and why you really should Sign up and Log in." - out.fm_backup_title = ""; // "Backup link" - out.fm_nameFile = ""; // "How would you like to name that file?" - out.fc_newfolder = ""; // "New folder" - out.fc_rename = ""; // "Rename" - out.fc_open = ""; // "Open" - out.fc_open_ro = ""; // "Open (read-only)" - out.fc_delete = ""; // "Delete" - out.fc_restore = ""; // "Restore" - out.fc_remove = ""; // "Delete permanently" - out.fc_empty = ""; // "Empty the trash" - out.fc_prop = ""; // "Properties" - out.fc_sizeInKilobytes = ""; // "Size in Kilobytes" - out.fo_moveUnsortedError = ""; // "You can't move a folder to the list of unsorted pads" - out.fo_existingNameError = ""; // "Name already used in that directory. Please choose another one." - out.fo_moveFolderToChildError = ""; // "You can't move a folder into one of its descendants" - out.fo_unableToRestore = ""; // "Unable to restore that file to its original location. You can try to move it to a new location." - out.fo_unavailableName = ""; // "A file or a folder with the same name already exist at the new location. Rename the element and try again." - out.login_login = ""; // "Log in" - out.login_makeAPad = ""; // "Create a pad anonymously" - out.login_nologin = ""; // "Browse local pads" - out.login_register = ""; // "Sign up" - out.logoutButton = ""; // "Log out" - out.settingsButton = ""; // "Settings" - out.login_username = ""; // "Username" - out.login_password = ""; // "Password" - out.login_confirm = ""; // "Confirm your password" - out.login_remember = ""; // "Remember me" - out.login_hashing = ""; // "Hashing your password, this might take some time." - out.login_hello = ""; // "Hello {0}," - out.login_helloNoName = ""; // "Hello," - out.login_accessDrive = ""; // "Access your drive" - out.login_orNoLogin = ""; // "or" - out.login_noSuchUser = ""; // "Invalid username or password. Try again, or sign up" - out.login_invalUser = ""; // "Username required" - out.login_invalPass = ""; // "Password required" - out.login_unhandledError = ""; // "An unexpected error occurred :(" - out.register_importRecent = ""; // "Import pad history (Recommended)" - out.register_acceptTerms = ""; // "I accept the terms of service" - out.register_passwordsDontMatch = ""; // "Passwords do not match!" - out.register_mustAcceptTerms = ""; // "You must accept the terms of service." - out.register_mustRememberPass = ""; // "We cannot reset your password if you forget it. It's very important that you remember it! Please check the checkbox to confirm." - out.register_header = ""; // "Welcome to CryptPad" - out.register_explanation = ""; // "

Lets go over a couple things first

" - out.register_writtenPassword = ""; // "I have written down my username and password, proceed" - out.register_cancel = ""; // "Go back" - out.register_warning = ""; // "Zero Knowledge means that we can't recover your data if you lose your password." - out.register_alreadyRegistered = ""; // "This user already exists, do you want to log in?" - out.settings_title = ""; // "Settings" - out.settings_save = ""; // "Save" - out.settings_backupTitle = ""; // "Backup or restore all your data" - out.settings_backup = ""; // "Backup" - out.settings_restore = ""; // "Restore" - out.settings_resetTitle = ""; // "Clean your drive" - out.settings_reset = ""; // "Remove all the files and folders from your CryptDrive" - out.settings_resetPrompt = ""; // "This action will remove all the pads from your drive.
Are you sure you want to continue?
Type “I love CryptPad” to confirm." - out.settings_resetDone = ""; // "Your drive is now empty!" - out.settings_resetError = ""; // "Incorrect verification text. Your CryptDrive has not been changed." - out.settings_resetTips = ""; // "Tips in CryptDrive" - out.settings_resetTipsButton = ""; // "Reset the available tips in CryptDrive" - out.settings_resetTipsDone = ""; // "All the tips are now visible again." - out.settings_importTitle = ""; // "Import this browser's recent pads in my CryptDrive" - out.settings_import = ""; // "Import" - out.settings_importConfirm = ""; // "Are you sure you want to import recent pads from this browser to your user account's CryptDrive?" - out.settings_importDone = ""; // "Import completed" - out.settings_userFeedbackHint1 = ""; // "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience." - out.settings_userFeedbackHint2 = ""; // "Your pad's content will never be shared with the server." - out.settings_userFeedback = ""; // "Enable user feedback" - out.settings_anonymous = ""; // "You are not logged in. Settings here are specific to this browser." - out.settings_publicSigningKey = ""; // "Public Signing Key" - out.settings_usage = ""; // "Usage" - out.settings_usageTitle = ""; // "See the total size of your pinned pads in MB" - out.settings_pinningNotAvailable = ""; // "Pinned pads are only available to registered users." - out.settings_pinningError = ""; // "Something went wrong" - out.settings_usageAmount = ""; // "Your pinned pads occupy {0}MB" - out.settings_logoutEverywhereTitle = ""; // "Log out everywhere" - out.settings_logoutEverywhere = ""; // "Log out of all other web sessions" - out.settings_logoutEverywhereConfirm = ""; // "Are you sure? You will need to log in with all your devices." - out.upload_serverError = ""; // "Server Error: unable to upload your file at this time." - out.upload_uploadPending = ""; // "You already have an upload in progress. Cancel it and upload your new file?" - out.upload_success = ""; // "Your file ({0}) has been successfully uploaded and added to your drive" - out.main_p2 = ""; // "This project uses the CKEditor Visual Editor, CodeMirror, and the ChainPad realtime engine." - out.main_howitworks_p1 = ""; // "CryptPad uses a variant of the Operational transformation algorithm which is able to find distributed consensus using a Nakamoto Blockchain, a construct popularized by Bitcoin. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad." - out.main_about_p2 = ""; // "If you have any questions or comments, you can tweet us, open an issue on github, come say hi on irc (irc.freenode.net), or send us an email." - out.main_info = ""; // "

Collaborate in Confidence


Grow your ideas together with shared documents while Zero Knowledge technology secures your privacy; even from us." - out.main_howitworks = ""; // "How It Works" - out.main_zeroKnowledge = ""; // "Zero Knowledge" - out.main_zeroKnowledge_p = ""; // "You don't have to trust that we won't look at your pads, with CryptPad's revolutionary Zero Knowledge Technology we can't. Learn more about how we protect your Privacy and Security." - out.main_writeItDown = ""; // "Write it down" - out.main_writeItDown_p = ""; // "The greatest projects come from the smallest ideas. Take down the moments of inspiration and unexpected ideas because you never know which one might be a breakthrough." - out.main_share = ""; // "Share the link, share the pad" - out.main_share_p = ""; // "Grow your ideas together: conduct efficient meetings, collaborate on TODO lists and make quick presentations with all your friends and all your devices." - out.main_organize = ""; // "Get organized" - out.main_organize_p = ""; // "With CryptPad Drive, you can keep your sights on what's important. Folders allow you to keep track of your projects and have a global vision of where things are going." - out.tryIt = ""; // "Try it out!" - out.main_richText = ""; // "Rich Text editor" - out.main_richText_p = ""; // "Edit rich text pads collaboratively with our realtime Zero Knowledge CkEditor application." - out.main_code = ""; // "Code editor" - out.main_code_p = ""; // "Edit code from your software collaboratively with our realtime Zero Knowledge CodeMirror application." - out.main_slide = ""; // "Slide editor" - out.main_slide_p = ""; // "Create your presentations using the Markdown syntax, and display them in your browser." - out.main_poll = ""; // "Polls" - out.main_poll_p = ""; // "Plan your meeting or your event, or vote for the best solution regarding your problem." - out.main_drive = ""; // "CryptDrive" - out.footer_applications = ""; // "Applications" - out.footer_contact = ""; // "Contact" - out.footer_aboutUs = ""; // "About us" - out.about = ""; // "About" - out.privacy = ""; // "Privacy" - out.contact = ""; // "Contact" - out.terms = ""; // "ToS" - out.blog = ""; // "Blog" - out.policy_title = ""; // "CryptPad Privacy Policy" - out.policy_whatweknow = ""; // "What we know about you" - out.policy_whatweknow_p1 = ""; // "As an application that is hosted on the web, CryptPad has access to metadata exposed by the HTTP protocol. This includes your IP address, and various other HTTP headers that can be used to identify your particular browser. You can see what information your browser is sharing by visiting WhatIsMyBrowser.com." - out.policy_whatweknow_p2 = ""; // "We use Kibana, an open source analytics platform, to learn more about our users. Kibana tells us about how you found CryptPad, via direct entry, through a search engine, or via a referral from another web service like Reddit or Twitter." - out.policy_howweuse = ""; // "How we use what we learn" - out.policy_howweuse_p1 = ""; // "We use this information to make better decisions about promoting CryptPad, by evaluating which of our past efforts were successful. Information about your location lets us know whether we should consider providing better support for languages other than English." - out.policy_howweuse_p2 = ""; // "Information about your browser (whether it's a desktop or mobile operating system) helps us make decisions when prioritizing feature improvements. Our development team is small, and we try to make choices that will improve as many users' experience as possible." - out.policy_whatwetell = ""; // "What we tell others about you" - out.policy_whatwetell_p1 = ""; // "We do not furnish to third parties the information that we gather or that you provide to us unless we are legally required to do so." - out.policy_links = ""; // "Links to other sites" - out.policy_links_p1 = ""; // "This site contains links to other sites, including those produced by other organizations. We are not responsible for the privacy practices or the contents of any outside sites. As a general rule, links to outside sites are launched in a new browser window, to make clear that you are leaving CryptPad.fr." - out.policy_ads = ""; // "Advertisement" - out.policy_ads_p1 = ""; // "We do not display any online advertising, though we may link to the bodies which are financing our research." - out.policy_choices = ""; // "Choices you have" - out.policy_choices_open = ""; // "Our code is open source, so you always have the option of hosting your own instance of CryptPad." - out.policy_choices_vpn = ""; // "If you want to use our hosted instance, but don't want to expose your IP address, you can protect your IP using the Tor browser bundle, or a VPN." - out.policy_choices_ads = ""; // "If you just want to block our analytics platform, you can use adblocking tools like Privacy Badger." - out.tos_title = ""; // "CryptPad Terms of Service" - out.tos_legal = ""; // "Please don't be malicious, abusive, or do anything illegal." - out.tos_availability = ""; // "We hope you find this service useful, but availability or performance cannot be guaranteed. Please export your data regularly." - out.tos_e2ee = ""; // "CryptPad contents can be read or modified by anyone who can guess or otherwise obtain the pad's fragment identifier. We recommend that you use end-to-end-encrypted (e2ee) messaging technology to share links, and assume no liability in the event that such a link is leaked." - out.tos_logs = ""; // "Metadata provided by your browser to the server may be logged for the purpose of maintaining the service." - out.tos_3rdparties = ""; // "We do not provide individualized data to third parties unless required to by law." - out.bottom_france = ""; // "Made with \"love\" in \"France\"" - out.bottom_support = ""; // "An \"XWiki Labs Project with the support of \"OpenPaaS-ng\"" - out.header_france = ""; // "With \"love\" from \"France\"/ by \"XWiki" - out.header_support = ""; // " \"OpenPaaS-ng\"" - out.header_logoTitle = ""; // "Go to the main page" - out.initialState = ""; // "

This is CryptPad, the Zero Knowledge realtime collaborative editor. Everything is saved as you type.
Share the link to this pad to edit with friends or use the  Share  button to share a read-only link which allows viewing but not editing.

Go ahead, just start typing...

 

" - out.codeInitialState = ""; // "/*\n This is the CryptPad Zero Knowledge collaborative code editor.\n What you type here is encrypted so only people who have the link can access it.\n You can choose the programming language to highlight and the UI color scheme in the upper right.\n*/" - out.slideInitialState = ""; // "# CryptSlide\n* This is a zero knowledge realtime collaborative editor.\n* What you type here is encrypted so only people who have the link can access it.\n* Even the server cannot see what you type.\n* What you see here, what you hear here, when you leave here, let it stay here.\n\n---\n# How to use\n1. Write your slides content using markdown syntax\n - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n2. Separate your slides with ---\n3. Click on the \"Play\" button to see the result - Your slides are updated in realtime" - out.driveReadmeTitle = ""; // "What is CryptDrive?" - out.readme_welcome = ""; // "Welcome to CryptPad !" - out.readme_p1 = ""; // "Welcome to CryptPad, this is where you can take note of things alone and with friends." - out.readme_p2 = ""; // "This pad will give you a quick walk through of how you can use CryptPad to take notes, keep them organized and work together on them." - out.readme_cat1 = ""; // "Get to know your CryptDrive" - out.readme_cat1_l1 = ""; // "Make a pad: In your CryptDrive, click {0} then {1} and you can make a pad." - out.readme_cat1_l2 = ""; // "Open Pads from your CryptDrive: double-click on a pad icon to open it." - out.readme_cat1_l3 = ""; // "Organize your pads: When you are logged in, every pad you access will be shown as in the {0} section of your drive." - out.readme_cat1_l3_l1 = ""; // "You can click and drag files into folders in the {0} section of your drive and make new folders." - out.readme_cat1_l3_l2 = ""; // "Remember to try right clicking on icons because there are often additional menus." - out.readme_cat1_l4 = ""; // "Put old pads in the trash: You can click and drag your pads into the {0} the same way you drag them into folders." - out.readme_cat2 = ""; // "Make pads like a pro" - out.edit = ""; // "edit" - out.view = ""; // "view" - out.readme_cat2_l1 = ""; // "The {0} button in your pad allows you to give access to collaborators to either {1} or to {2} the pad." - out.readme_cat2_l2 = ""; // "Change the title of the pad by clicking on the pencil" - out.readme_cat3 = ""; // "Discover CryptPad apps" - out.readme_cat3_l1 = ""; // "With CryptPad code editor, you can collaborate on code like Javascript and markdown like HTML and Markdown" - out.readme_cat3_l2 = ""; // "With CryptPad slide editor, you can make quick presentations using Markdown" - out.readme_cat3_l3 = ""; // "With CryptPoll you can take quick votes, especially for scheduling meetings which fit with everybody's calendar" - out.tips = ""; // {"lag":"The green icon in the upper right shows the quality of your internet connection to the CryptPad server.","shortcuts":"`ctrl+b`, `ctrl+i` and `ctrl+u` are quick shortcuts for bold, italic and underline.","indent":"In numbered and bulleted lists, you can use tab or shift+tab to quickly increase or decrease indentation.","title":"You can set the title of your pad by clicking the top center.","store":"Every time you visit a pad, if you're logged in it will be saved to your CryptDrive.","marker":"You can highlight text in a pad using the \"marker\" item in the styles dropdown menu."} - out.feedback_about = ""; // "If you're reading this, you were probably curious why CryptPad is requesting web pages when you perform certain actions" - out.feedback_privacy = ""; // "We care about your privacy, and at the same time we want CryptPad to be very easy to use. We use this file to figure out which UI features matter to our users, by requesting it along with a parameter specifying which action was taken." - out.feedback_optout = ""; // "If you would like to opt out, visit your user settings page, where you'll find a checkbox to enable or disable user feedback" + out.type = {}; + out.pad = "Rich text"; + out.code = "Code"; + out.poll = "Poll"; + out.slide = "Presentation"; + out.drive = "Drive"; + out.whiteboard = "Whiteboard"; + out.file = "File"; + out.media = "Media"; + out.button_newpad = "Filă Text Nouă"; + out.button_newcode = "Filă Cod Nouă"; + out.button_newpoll = "Sondaj Nou"; + out.button_newslide = "Prezentare Nouă"; + out.button_newwhiteboard = "Fila Desen Nouă"; + out.updated_0_common_connectionLost = "Conexiunea la server este pierdută
Până la revenirea conexiunii, vei fi în modul citire"; + out.common_connectionLost = out.updated_0_common_connectionLost; + out.websocketError = "Conexiune inexistentă către serverul websocket..."; + out.typeError = "Această filă nu este compatibilă cu aplicația aleasă"; + out.onLogout = "Nu mai ești autentificat, apasă aici să te autentifici
sau apasă Escapesă accesezi fila în modul citire."; + out.wrongApp = "Momentan nu putem arăta conținutul sesiunii în timp real în fereastra ta. Te rugăm reîncarcă pagina."; + out.loading = "Încarcă..."; + out.error = "Eroare"; + + out.saved = "Salvat"; + out.synced = "Totul a fost salvat"; + out.deleted = "Pad șters din CryptDrive-ul tău"; + out.disconnected = "Deconectat"; + out.synchronizing = "Se sincronizează"; + out.reconnecting = "Reconectare..."; + out.lag = "Decalaj"; + out.readonly = "Mod citire"; + out.anonymous = "Anonim"; + out.yourself = "Tu"; + out.anonymousUsers = "editori anonimi"; + out.anonymousUser = "editor anonim"; + out.users = "Utilizatori"; + out.and = "Și"; + out.viewer = "privitor"; + out.viewers = "privitori"; + out.editor = "editor"; + out.editors = "editori"; + out.language = "Limbă"; + out.upgrade = "Actualizare"; + out.upgradeTitle = "Actualizează-ți contul pentru a mări limita de stocare"; + out.MB = "MB"; + out.greenLight = "Totul funcționează corespunzător"; + out.orangeLight = "Conexiunea lentă la internet îți poate afecta experiența"; + out.redLight = "Ai fost deconectat de la sesiune"; + out.pinLimitReached = "Ai atins limita de stocare"; + out.pinLimitReachedAlert = "Ai atins limita de stocare. Noile pad-uri nu vor mai fi stocate în CryptDrive.
Pentru a rezolva această problemă, poți să nlături pad-uri din CryptDrive-ul tău (incluzând gunoiul) sau să subscrii la un pachet premium pentru a-ți extinde spațiul de stocare."; + out.pinLimitNotPinned = "Ai atins limita de stocare.
Acest pad nu va fi stocat n CryptDrive-ul tău."; + out.pinLimitDrive = "Ai atins limita de stocare.
Nu poți să creezi alte pad-uri."; + out.importButtonTitle = "Importă un pad dintr-un fișier local"; + out.exportButtonTitle = "Exportă pad-ul acesta către un fișier local"; + out.exportPrompt = "Cum ai vrea să îți denumești fișierul?"; + out.changeNamePrompt = "Schimbă-ți numele (lasă necompletat dacă vrei să fii anonim): "; + out.user_rename = "Schimbă numele afișat"; + out.user_displayName = "Nume afișat"; + out.user_accountName = "Nume cont"; + out.clickToEdit = "Click pentru editare"; + out.forgetButtonTitle = "Mută acest pad la gunoi"; + out.forgetPrompt = "Click-ul pe OK va muta acest pad la gunoi. Ești sigur?"; + out.movedToTrash = "Acest pad a fost mutat la gunoi.
Acesează-mi Drive-ul"; + out.shareButton = "Distribuie"; + out.shareSuccess = "Link copiat în clipboard"; + out.newButton = "Nou"; + out.newButtonTitle = "Crează un nou pad"; + out.saveTemplateButton = "Salvează ca șablon"; + out.saveTemplatePrompt = "Alege un titlu pentru șablon"; + out.templateSaved = "Șablon salvat!"; + out.selectTemplate = "Selectează un șablon sau apasă escape"; + out.presentButtonTitle = "Intră în modul de prezentare"; + out.presentSuccess = "Apasă ESC pentru a ieși din modul de prezentare"; + out.backgroundButtonTitle = "Schimbă culoarea de fundal din prezentare"; + out.colorButtonTitle = "Schimbă culoarea textului în modul de prezentare"; + out.printButton = "Printează (enter)"; + out.printButtonTitle = "Printează-ți slide-urile sau exportă-le ca fișier PDF"; + out.printOptions = "Opțiuni schemă"; + out.printSlideNumber = "Afișează numărul slide-ului"; + out.printDate = "Afișează data"; + out.printTitle = "Afișează titlul pad-ului"; + out.printCSS = "Reguli de stil personalizate (CSS):"; + out.printTransition = "Permite tranziția animațiilor"; + out.slideOptionsTitle = "Personalizează-ți slide-urile"; + out.slideOptionsButton = "Salvează (enter)"; + out.editShare = "Editează link-ul"; + out.editShareTitle = "Copiază link-ul de editare în clipboard"; + out.editOpen = "Deschide link-ul de editare într-o nouă filă"; + out.editOpenTitle = "Deschide acest pad în modul de editare într-o nouă filă"; + out.viewShare = "Link în modul citire"; + out.viewShareTitle = "Copiază link-ul în modul de citire în clipboard"; + out.viewOpen = "Deschide link-ul în modul de citire într-o filă nouă"; + out.viewOpenTitle = "Deschide acest pad în modul de citire într-o nouă filă"; + out.notifyJoined = "{0} s-au alăturat sesiunii colaborative"; + out.notifyRenamed = "{0} e cunoscut ca {1}"; + out.notifyLeft = "{0} au părăsit sesiunea colaborativă"; + out.okButton = "OK (enter)"; + out.cancel = "Anulează"; + out.cancelButton = "Anulează (esc)"; + out.historyButton = "Afișează istoricul documentului"; + out.history_next = "Mergi la versiunea următoare"; + out.history_prev = "Mergi la versiunea trecută"; + out.history_goTo = "Mergi la sesiunea selectată"; + out.history_close = "Înapoi"; + out.history_closeTitle = "Închide istoricul"; + out.history_restore = "Restabilește"; + out.history_restoreTitle = "Restabilește versiunea selectată a documentului"; + out.history_restorePrompt = "Ești sigur că vrei să înlocuiești versiunea curentă a documentului cu cea afișată?"; + out.history_restoreDone = "Document restabilit"; + out.history_version = "Versiune:"; + out.poll_title = "Zero Knowledge Selector Dată"; + out.poll_subtitle = "Zero Knowledge, realtime programare"; + out.poll_p_save = "Setările tale sunt actualizate instant, așa că tu nu trebuie să salvezi."; + out.poll_p_encryption = "Tot conținutul tău este criptat ca doar persoanele cărora tu le dai link-ul să aibă acces. Nici serverul nu poate să vadă ce modifici."; + out.wizardLog = "Click pe butonul din dreapta sus pentru a te ntoarce la sondajul tău"; + out.wizardTitle = "Folosește wizard-ul pentru a crea sondajul tău"; + out.wizardConfirm = "Ești pregătit să adaugi aceste opțiuni la sondajul tău?"; + out.poll_publish_button = "Publică"; + out.poll_admin_button = "Admin"; + out.poll_create_user = "Adaugă un nou utilizator"; + out.poll_create_option = "Adaugă o nouă opțiune"; + out.poll_commit = "Comite"; + out.poll_closeWizardButton = "Închide wizard-ul"; + out.poll_closeWizardButtonTitle = "Închide wizard-ul"; + out.poll_wizardComputeButton = "Calculează Opțiunile"; + out.poll_wizardClearButton = "Curăță Tabelul"; + out.poll_wizardDescription = "Crează automat un număr de opțiuni întroducând orice număr de zile sau intervale orare"; + + out.poll_wizardAddDateButton = "+ Zi"; + out.poll_wizardAddTimeButton = "+ Ore"; + out.poll_optionPlaceholder = "Opțiune"; + out.poll_userPlaceholder = "Numele tău"; + out.poll_removeOption = "Ești sigur că vrei să îndepărtezi această opțiune?"; + out.poll_removeUser = "Ești sigur că vrei să îndepărtezi aceast utilizator?"; + out.poll_titleHint = "Titlu"; + out.poll_descriptionHint = "Descrie sondajul, și apoi folosește butonul 'publică' când ai terminat. Orice utilizator care are link-ul poate modifica descrierea, dar descurajăm această practică."; + out.canvas_clear = "Curăță"; + out.canvas_delete = "Curăță selecția"; + out.canvas_disable = "Dezactivează modul desen"; + out.canvas_enable = "Activează modul desen"; + out.canvas_width = "Lățime"; + out.canvas_opacity = "Opacitate"; + out.fm_rootName = "Documente"; + out.fm_trashName = "Gunoi"; + out.fm_unsortedName = "Fișiere nesortate"; + out.fm_filesDataName = "Toate fișierele"; + out.fm_templateName = "Șabloane"; + out.fm_searchName = "Caută"; + out.fm_searchPlaceholder = "Caută..."; + out.fm_newButton = "Nou"; + out.fm_newButtonTitle = "Crează un nou pad sau folder"; + out.fm_newFolder = "Folder nou"; + out.fm_newFile = "Pad nou"; + out.fm_folder = "Folder"; + out.fm_folderName = "Numele folderului"; + out.fm_numberOfFolders = "# de foldere"; + out.fm_numberOfFiles = "# of files"; + out.fm_fileName = "Nume filă"; + out.fm_title = "Titlu"; + out.fm_type = "Tip"; + out.fm_lastAccess = "Ultima accesare"; + out.fm_creation = "Creare"; + out.fm_forbidden = "Acțiune interzisă"; + out.fm_originalPath = "Ruta inițială"; + out.fm_openParent = "Arată în folder"; + out.fm_noname = "Document nedenumit"; + out.fm_emptyTrashDialog = "Ești sigur că vrei să golești coșul de gunoi?"; + out.fm_removeSeveralPermanentlyDialog = "Ești sigur că vrei să ștergi pentru totdeauna aceste {0} elemente din coșul de gunoi?"; + out.fm_removePermanentlyDialog = "Ești sigur că vrei să ștergi acest element pentru totdeauna?"; + out.fm_removeSeveralDialog = "Ești sigur că vrei să muți aceste {0} elemente la coșul de gunoi?"; + out.fm_removeDialog = "Ești sigur că vrei să muți {0} la gunoi?"; + out.fm_restoreDialog = "Ești sigur că vrei să restabilești {0} în locația trecută?"; + out.fm_unknownFolderError = "Ultima locație vizitată sau cea selectată nu mai există. Deschidem fișierul părinte..."; + out.fm_contextMenuError = "Nu putem deschide meniul de context pentru acest element. Dacă problema persistă, reîncarcă pagina."; + out.fm_selectError = "Nu putem selecta elementul vizat. Dacă problema persistă, reîncarcă pagina."; + out.fm_categoryError = "Nu putem deschide categoria selectată, afișează sursa."; + out.fm_info_root = "Crează câte foldere tip cuib ai nevoie pentru a-ți sorta fișierele."; + out.fm_info_unsorted = "Conține toate fișierele pe care le-ai vizitat și nu sunt sortate în \"Documente\" sau mutate în \"Gunoi\"."; + out.fm_info_template = "Conține toate pad-urile stocate ca șabloane și pe care le poți refolosi atunci când creezi un nou pad."; + out.fm_info_trash = "Fișierele șterse din gunoi vor fi șterse și din \"Toate fișierele\", făcând imposibilă recuperarea fișierelor din managerul de fișiere."; + out.fm_info_allFiles = "Conține toate fișierele din \"Documente\", \"Nesortate\" și \"Gunoi\". Poți să muți sau să ștergi fișierele aici."; + out.fm_info_login = "Loghează-te"; + out.fm_info_register = "Înscrie-te"; + out.fm_info_anonymous = "Nu ești logat cu un cont valid așa că aceste pad-uri vor fi șterse (află de ce). Înscrie-te sau Loghează-te pentru a le salva."; + out.fm_alert_backupUrl = "Link copie de rezervă pentru acest drive.
Este foarte recomandat să o păstrezi pentru tine.
Poți să o folosești pentru a recupera toate fișierele în cazul în care memoria browserului tău este șterge..
Oricine are linkul poate să editeze sau să îndepărteze toate fișierele din managerul tău de documente.
"; + out.fm_alert_anonymous = "Salut, momentan folosești CryptPad în mod anonim. Este ok, doar că fișierele tale vor fi șterse după o perioadă de inactivitate. Am dezactivat caracteristicile avansate ale drive-ului pentru utilizatorii anonimi pentru a face clar faptul că stocare documentelor acolo nu este o metodă sigură. Poți să citești mai multe despre motivarea noastră și despre ce de trebuie să te Înregistrezi si sa te Loghezi."; + out.fm_backup_title = "Link de backup"; + out.fm_nameFile = "Cum ai vrea să numești fișierul?"; + out.fc_newfolder = "Folder nou"; + out.fc_rename = "Redenumește"; + out.fc_open = "Deschide"; + out.fc_open_ro = "Deschide (modul citire)"; + out.fc_delete = "Șterge"; + out.fc_restore = "Restaurează"; + out.fc_remove = "Șterge permanent"; + out.fc_empty = "Curăță coșul"; + out.fc_prop = "Proprietăți"; + out.fc_sizeInKilobytes = "Dimensiune n Kilobytes"; + out.fo_moveUnsortedError = "Nu poți să muți un folder la lista de pad-uri nesortate"; + out.fo_existingNameError = "Numele ales este deja folosit în acest director. Te rugăm să alegi altul."; + out.fo_moveFolderToChildError = "Nu poți să muți un folder într-unul dintre descendenții săi"; + out.fo_unableToRestore = "Nu am reușit să restaurăm fișierul în locația de origine. Poți să ncerci să îl muți într-o nouă locație."; + out.fo_unavailableName = "Un fișier sau un folder cu același nume există deja în locația nouă. Redenumește elementul și încearcă din nou."; + out.login_login = "Loghează-te"; + out.login_makeAPad = "Crează un pad în modul anonim"; + out.login_nologin = "Răsfoiește pad-urile locale"; + out.login_register = "Înscrie-te"; + out.logoutButton = "Deloghează-te"; + out.settingsButton = "Setări"; + out.login_username = "Nume utilizator"; + out.login_password = "Parolă"; + out.login_confirm = "Confirmă parola"; + out.login_remember = "Ține-mă minte"; + out.login_hashing = "Încriptăm parola, o să mai dureze."; + out.login_hello = "Salut {0},"; + out.login_helloNoName = "Salut,"; + out.login_accessDrive = "Acesează-ți drive-ul"; + out.login_orNoLogin = "sau"; + out.login_noSuchUser = "Nume de utilizator sau parolă invalide. Încearcă din nou sau înscrie-te."; + out.login_invalUser = "Nume utilizator cerut"; + out.login_invalPass = "Parolă cerută"; + out.login_unhandledError = "O eroare neașteptată a avut loc emoticon_unhappy"; + out.register_importRecent = "Importă istoricul pad-ului (Recomandat)"; + out.register_acceptTerms = "Accept termenii serviciului"; + out.register_passwordsDontMatch = "Parolele nu se potrivesc!"; + out.register_mustAcceptTerms = "Trebuie să accepți termenii serviciului"; + out.register_mustRememberPass = "Nu putem să îți resetăm parola dacă o uiți. Este foarte important să o ții minte! Bifează căsuța pentru a confirma."; + out.register_header = "Bine ai venit în CryptPad"; + out.register_explanation = "

Hai să stabilim câteva lucruri, mai întâi

"; + out.register_writtenPassword = "Mi-am notat numele de utilizator și parola, înaintează."; + out.register_cancel = "Întoarce-te"; + out.register_warning = "Zero Knowledge înseamnă că noi nu îți putem recupera datele dacă îți pierzi parola."; + out.register_alreadyRegistered = "Acest user există deja, vrei să te loghezi?"; + out.settings_title = "Setări"; + out.settings_save = "Salvează"; + out.settings_backupTitle = "Fă o copie de rezervă sau restaurează toate datele"; + out.settings_backup = "Copie de rezervă"; + out.settings_restore = "Restaurează"; + out.settings_resetTitle = "Curăță-ți drive-ul"; + out.settings_reset = "Îndepărtează toate fișierele și folderele din CryptPad-ul tău."; + out.settings_resetPrompt = "Această acțiune va indepărta toate pad-urile din drive-ul tău.
Ești sigur că vrei să continui?
Tastează “Iubesc CryptPad” pentru a confirma."; + out.settings_resetDone = "Drive-ul tău este acum gol!"; + out.settings_resetError = "Text de verificare incorect. CryptPad-ul tău nu a fost schimbat."; + out.settings_resetTips = "Sfaturi în CryptDrive"; + out.settings_resetTipsButton = "Resetează sfaturile disponibile în CryptDrive"; + out.settings_resetTipsDone = "Toate sfaturile sunt vizibile din nou."; + out.settings_importTitle = "Importă pad-urile recente ale acestui browser n CryptDrive-ul meu"; + out.settings_import = "Importă"; + out.settings_importConfirm = "Ești sigur că vrei să imporți pad-urile recente ale acestui browser în contul tău de CryptDrive?"; + out.settings_importDone = "Import complet"; + out.settings_userFeedbackHint1 = "CryptPad oferă niște feedback foarte simplu serverului, pentru a ne informa cum putem să îți îmbunătățim experiența voastră."; + out.settings_userFeedbackHint2 = "Conținutul pad-ului tău nu va fi împărțit cu serverele."; + out.settings_userFeedback = "Activează feedback"; + out.settings_anonymous = "Nu ești logat. Setările sunt specifice browser-ului."; + out.settings_publicSigningKey = "Cheia de semnătură publică"; + out.settings_usage = "Uzaj"; + out.settings_usageTitle = "Vezi dimensiunea totală a pad-urilor fixate în MB"; + out.settings_pinningNotAvailable = "Pad-urile fixate sunt disponibile doar utilizatorilor înregistrați."; + out.settings_pinningError = "Ceva nu a funcționat"; + out.settings_usageAmount = "Pad-urile tale fixate ocupă {0}MB"; + out.settings_logoutEverywhereTitle = "Deloghează-te peste tot"; + out.settings_logoutEverywhere = "Deloghează-te din toate sesiunile web"; + out.settings_logoutEverywhereConfirm = "Ești sigur? Va trebui să te loghezi, din nou, pe toate device-urile tale."; + out.upload_serverError = "Eroare de server: fișierele tale nu pot fi încărcate la momentul acesta."; + out.upload_uploadPending = "Ai deja o încărcare în desfășurare. Anulezi și încarci noul fișier?"; + out.upload_success = "Fișierul tău ({0}) a fost ncărcat și adăugat la drive-ul tău cu succes."; + out.main_p2 = "Acest proiect folosește CKEditor Visual Editor, CodeMirror, și ChainPad un motor în timp real."; + out.main_howitworks_p1 = "CryptPad folosește o variantă a algoritmului de Operational transformation care este capabil să găsescă consens distribuit folosind Nakamoto Blockchain, o construcție popularizată de Bitcoin. Astfel algoritmul poate evita nevoia ca serverul central să rezove conflicte, iar serverul nu este interesat de conținutul care este editat în pad."; + out.main_about_p2 = "Dacă ai orice fel de întrebare sau comentariu, poți să ne dai un tweet, semnalezi o problemă on github, spui salut pe IRC (irc.freenode.net), sau trimiți un email."; + out.main_info = "

Colaborează în siguranță


Dezvoltă-ți ideile împreună cu documentele partajate în timp ce tehnologia Zero Knowledge îți păstrează securitatea; chiar și de noi."; + out.main_howitworks = "Cum funcționează"; + out.main_zeroKnowledge = "Zero Knowledge"; + out.main_zeroKnowledge_p = "Nu trebuie să ne crezi că nu ne uităm la pad-urile tale, cu tehnologia revoluționară Zero Knowledge a CryptPad nu putem. Învață mai multe despre cum îți protejăm Intimitate și Securitate."; + out.main_writeItDown = "Notează"; + out.main_writeItDown_p = "Cele mai importante proiecte vin din idei mici. Notează-ți momentele de inspirație și ideile neașteptate pentru că nu știi niciodată care ar putea fi noua mare descoperire."; + out.main_share = "Partajează link-ul, partajează pad-ul"; + out.main_share_p = "Dezvoltă-ți ideile împreună: organizează întâlniri eficiente, colaborează pe liste TODO și fă prezentări scurte cu toți prietenii tăi și device-urile tale."; + out.main_organize = "Organizează-te"; + out.main_organize_p = "Cu CryptPad Drive, poți să stai cu ochii pe ce este important. Folderele îți permit să ții evidența proiectelor tale și să ai o viziune globală asupra evoluției lucrurilor."; + out.tryIt = "Testează!"; + out.main_richText = "Rich Text editor"; + out.main_richText_p = "Editează texte complexe în mod colaborativ cu Zero Knowledge în timp real. CkEditor application."; + out.main_code = "Editor cod"; + out.main_code_p = "Editează cod din softul tău, în mod colaborativ, cu Zero Knowledge în timp real.CodeMirror application."; + out.main_slide = "Editor slide-uri"; + out.main_slide_p = "Crează-ți prezentări folosind sintaxa Markdown, și afișează-le în browser-ul tău."; + out.main_poll = "Sondaj"; + out.main_poll_p = "Plănuiește întâlniri sau evenimente, sau votează pentru cea mai bună soluție pentru problema ta."; + out.main_drive = "CryptDrive"; + out.footer_applications = "Aplicații"; + out.footer_contact = "Contact"; + out.footer_aboutUs = "Despre noi"; + out.about = "Despre"; + out.privacy = "Privacy"; + out.contact = "Contact"; + out.terms = "ToS"; + out.blog = "Blog"; + out.policy_title = "Politica de confidențialitate CryptPad"; + out.policy_whatweknow = "Ce știm despre tine"; + out.policy_whatweknow_p1 = "Ca o aplicație care este găzduită online, CryptPad are acces la metadatele expuse de protocolul HTTP. Asta include adresa IP-ului tău, și alte titluri HTTP care pot fi folosite ca să identifice un browser. Poți să vezi ce informații împărtășește browser-ul tău vizitând WhatIsMyBrowser.com."; + out.policy_whatweknow_p2 = "Folosim Kibana, o platformă open source, pentru a afla mai multe despre utilizatorii noștri. Kibana ne spune despre cum ai găsit CryptPad, căutare directă, printr-un motor de căutare, sau prin recomandare de la un alt serviciu online ca Reddit sau Twitter."; + out.policy_howweuse = "Cum folosim ce aflăm"; + out.policy_howweuse_p1 = "Folosim aceste informații pentru a lua decizii mai bune în promovarea CryptPad, prin evaluarea eforturilor trecute care au fost de succes. Informațiile despre locația ta ne ajută să aflăm dacă ar trebui să oferim suport pentru alte limbi, pe lângă engleză."; + out.policy_howweuse_p2 = "Informațiile despre browser-ul tău (dacă este bazat pe un sistem de operare desktop sau mobil) ne ajută să luăm decizii când prioritizăm viitoarele îmbunătățiri. Echipa noastră de dezvoltare este mică, și încercăm să facem alegeri care să îmbunătățească experiența câtor mai mulți utilizatori."; + + out.policy_whatwetell = "Ce le spunem altora despre tine"; + out.policy_whatwetell_p1 = "Nu furnizăm informațiile obținute terților, decât dacă ne este cerut în mod legal."; + out.policy_links = "Link-uri către alte site-uri"; + out.policy_links_p1 = "Acest site conține link-uri către alte site-uri, incluzându-le pe cele produse de alte organizații. Nu suntem responsabili pentru practicile de intimitate sau pentru conținutul site-urilor externe. Ca regulă generală, link-urile către site-uri externe sunt deschise ntr-o fereastră noup, pentru a face clar faptul că părăsiți CryptPad.fr."; + out.policy_ads = "Reclame"; + out.policy_ads_p1 = "Nu afișăm nici o formă de publicitate online, dar s-ar putea să atașăm link-uri către instituțiile care ne finanțează cerecetarea."; + out.policy_choices = "Ce alegeri ai"; + out.policy_choices_open = "Codul nostru este open source, așa că tu ai mereu posibilitatea de a-ți găzdui propria instanță de CryptPad."; + out.policy_choices_vpn = "Dacă vrei să folosești instanța găzduită de noi, dar nu vrei să îți expui IP-ul, poți să îl protejezi folosind Tor browser bundle, sau VPN."; + out.policy_choices_ads = "Dacă vrei doar să blochezi platforma noastră de analiză, poți folosi soluții de adblocking ca Privacy Badger."; + out.tos_title = "CryptPad Termeni de Utilizare"; + out.tos_legal = "Te rugăm să nu fii rău intenționat, abuziv, sau să faci orice ilegal."; + out.tos_availability = "Sperăm că o să găsești acest serviciu util, dar disponibilitatea sau performanța nu poate fi garantată. Te rugăm să îți exporți datele n mod regulat."; + out.tos_e2ee = "Conținutul CryptPad poate fi citit sau modificat de oricine care poate ghici sau obține fragmentul identificator al pad-ului. Recomandăm să folosești soluții de comunicare criptate end-to-end-encrypted (e2ee) pentru a partaja link-uri, evitând orice risc în cazul unei scurgeri de informații."; + out.tos_logs = "Metadatele oferite de browser-ul tău serverului ar putea fi înscrise în scopul de a menține serviciul."; + out.tos_3rdparties = "Nu oferim date personale terților, decât dacă ne sunt solicitate prin lege."; + out.bottom_france = "Realizat cu \"love\" n \"Franța\""; + out.bottom_support = "Un proiect al \"XWiki Labs Project cu susținerea \"OpenPaaS-ng\""; + out.header_france = "With \"love\" from \"Franța\"/ by \"XWiki"; + out.header_support = " \"OpenPaaS-ng\""; + out.header_logoTitle = "Mergi la pagina principală"; + out.initialState = "

Acesta este CryptPad, editorul colaborativ bazat pe tehnologia Zero Knowledge în timp real. Totul este salvat pe măsură ce scrii.
Partajează link-ul către acest pad pentru a edita cu prieteni sau folosește  Share  butonul pentru a partaja read-only link permițând vizualizarea dar nu și editarea.

Îndrăznește, începe să scrii...

 

"; + out.codeInitialState = "/*\n Acesta este editorul colaborativ de cod bazat pe tehnologia Zero Knowledge CryptPad.\n Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n Poți să alegi ce limbaj de programare pus n evidență și schema de culori UI n dreapta sus.\n*/"; + out.slideInitialState = "# CryptSlide\n* Acesta este un editor colaborativ bazat pe tehnologia Zero Knowledge.\n* Ce scrii aici este criptat, așa că doar oamenii care au link-ul pot să-l acceseze.\n* Nici măcar serverele nu au acces la ce scrii tu.\n* Ce vezi aici, ce auzi aici, atunci când pleci, lași aici.\n\n-\n# Cum se folosește\n1. Scrie-ți conținutul slide-urilor folosind sintaxa markdown\n - Află mai multe despre sintaxa markdown [aici](http://www.markdowntutorial.com/)\n2. Separă-ți slide-urile cu -\n3. Click pe butonul \"Play\" pentru a vedea rezultatele - Slide-urile tale sunt actualizate în timp real."; + out.driveReadmeTitle = "Ce este CryptDrive?"; + out.readme_welcome = "Bine ai venit n CryptPad !"; + out.readme_p1 = "Bine ai venit în CryptPad, acesta este locul unde îți poți lua notițe, singur sau cu prietenii."; + out.readme_p2 = "Acest pad o să îți ofere un scurt ghid în cum poți să folosești CryptPad pentru a lua notițe, a le ține organizate și a colabora pe ele."; + out.readme_cat1 = "Descoperă-ți CryptDrive-ul"; + out.readme_cat1_l1 = "Crează un pad: În CryptDrive-ul tău, dă click {0} apoi {1} și poți să creezi un pad."; + out.readme_cat1_l2 = "Deschide pad-urile din CryptDrive-ul tău: doublu-click pe iconița unui pad pentru a-l deschide."; + out.readme_cat1_l3 = "Organizează-ți pad-urile: Când ești logat, orice pad accesezi va fi afișat ca în secțiunea {0} a drive-ului tău."; + out.readme_cat1_l3_l1 = "Poți să folosești funcția click and drag pentru a muta fișierele în folderele secțiunii {0} a drive-ului tău și pentru a crea noi foldere."; + out.readme_cat1_l3_l2 = "Ține minte să încerci click-dreapta pe iconițe pentru că există și meniuri adiționale."; + out.readme_cat1_l4 = "Pune pad-urile vechi în gunoi. Poți să folosești funcția click and drag pe pad-uri în categoria {0} la fel ca și în cazul folderelor."; + out.readme_cat2 = "Crează pad-uri ca un profesionist"; + out.edit = "editează"; + out.view = "vezi"; + out.readme_cat2_l1 = "Butonul {0} din pad-ul tău dă accesul colaboratorilor tăi să {1} sau să {2} pad-ul."; + out.readme_cat2_l2 = "Schimbă titlul pad-ului dând click pe creion"; + out.readme_cat3 = "Descoperă aplicațiile CryptPad"; + out.readme_cat3_l1 = "Cu editorul de cod CryptPad, poți colabora pe cod ca Javascript și markdown ca HTML și Markdown"; + out.readme_cat3_l2 = "Cu editorul de slide-uri CryptPad, poți să faci prezentări scurte folosind Markdown"; + out.readme_cat3_l3 = "Cu CryptPoll poți să organizezi votări rapide, mai ales pentru a programa ntâlniri care se potrivesc calendarelor tuturor"; + + out.tips = { }; + out.tips.lag = "Iconița verde din dreapta-sus arată calitatea conexiunii internetului tău la serverele CryptPad."; + out.tips.shortcuts = "`ctrl+b`, `ctrl+i` and `ctrl+u` sunt scurtături pentru bold, italic și underline."; + out.tips.indentare = "În listele cu bulină sau cele numerotate, poți folosi tab sau shift+tab pentru a mări sau micșora indentarea."; + out.tips.titlu = "Poți seta titlul pad-urilor tale prin click pe centru sus."; + out.tips.stocare = "De fiecare dată când vizitezi un pad, dacă ești logat va fi salvat pe CryptDrive-ul tău."; + out.tips.marker = "Poți sublinia text într-un pad folosind itemul \"marker\" n meniul de stiluri."; + + out.feedback_about = "Dacă citești asta, probabil că ești curios de ce CryptPad cere pagini web atunci când întreprinzi anumite acțiuni"; + out.feedback_privacy = "Ne pasă de intimitatea ta, si în același timp vrem să păstrăm CryptPad ușor de folosit. Folosim acest fișier pentru a ne da seama care beneficii UI contează cel mai mult pentru utilizatori, cerându-l alături de un parametru specific atunci când acțiunea se desfășoară"; + out.feedback_optout = "Dacă vrei să ieși, vizitează setările de pe pagina ta de user, unde vei găsi o căsuță pentru a activa sau dezactiva feedback-ul de la user"; return out; }); diff --git a/customize.dist/translations/messages.zh.js b/customize.dist/translations/messages.zh.js new file mode 100644 index 000000000..81560c69a --- /dev/null +++ b/customize.dist/translations/messages.zh.js @@ -0,0 +1,544 @@ +define(function () { + var out = {}; + // translations must set this key for their language to be available in + // the language dropdowns that are shown throughout Cryptpad's interface + + out._languageName = 'Chinese'; + + out.main_title = "CryptPad: 零知識, 即時協作編寫"; + out.main_slogan = "團結就是力量 - 合作是關鍵"; // TODO remove? + + out.type = {}; + out.type.pad = '富文本'; + out.type.code = '編碼'; + out.type.poll = '投票'; + out.type.slide = '投影片簡報'; + out.type.drive = '磁碟'; + out.type.whiteboard = '白板'; + out.type.file = '檔案'; + out.type.media = '多媒體'; + + out.button_newpad = '富文件檔案'; + out.button_newcode = '新代碼檔案'; + out.button_newpoll = '新投票調查'; + out.button_newslide = '新簡報'; + out.button_newwhiteboard = '新白板'; + + // NOTE: We want to update the 'common_connectionLost' key. + // Please do not add a new 'updated_common_connectionLostAndInfo' but change directly the value of 'common_connectionLost' + out.updated_0_common_connectionLost = "伺服器連線中斷
現在是唯讀狀態,直到連線恢復正常。"; + out.common_connectionLost = out.updated_0_common_connectionLost; + + out.websocketError = '無法連結上 websocket 伺服器...'; + out.typeError = "這個編輯檔與所選的應用程式並不相容"; + out.onLogout = '你已登出, 點擊這裏 來登入
或按Escape 來以唯讀模型使用你的編輯檔案'; + out.wrongApp = "無法在瀏覽器顯示即時期間的內容,請試著再重新載入本頁。"; + + out.loading = "載入中..."; + out.error = "錯誤"; + out.saved = "儲存"; + out.synced = "所有資料已儲存好了"; + out.deleted = "自 CryptDrive 刪除檔案"; + + out.disconnected = '已斷線'; + out.synchronizing = '同步中'; + out.reconnecting = '重新連結...'; + out.lag = 'Lag'; + out.readonly = '唯讀'; + out.anonymous = "匿名"; + out.yourself = "你自己"; + out.anonymousUsers = "匿名的編輯群"; + out.anonymousUser = "匿名的編輯群者"; + out.users = "用戶"; + out.and = "與"; + out.viewer = "檢視者"; + out.viewers = "檢視群"; + out.editor = "編輯者"; + out.editors = "編輯群"; + + out.language = "語言"; + + out.comingSoon = "即將上市..."; + + out.newVersion = 'CryptPad 已更新!
' + + '檢查最新版本有什麼新功能:
'+ + 'CryptPad新發佈記事 {0}'; + + out.upgrade = "昇級"; + out.upgradeTitle = "昇級帳戶以取得更多的儲存空間"; + out.MB = "MB"; + out.GB = "GB"; + out.KB = "KB"; + + out.formattedMB = "{0} MB"; + out.formattedGB = "{0} GB"; + out.formattedKB = "{0} KB"; + + out.greenLight = "每件事都很順利"; + out.orangeLight = "連線速度慢可能會影響用戶體驗"; + out.redLight = "你這段期間的連線已中斷"; + + out.pinLimitReached = "你已達到儲存容量上限"; + out.updated_0_pinLimitReachedAlert = "你已達到儲存容量上限,新檔案不會儲存到你的 CryptDrive.
" + + '要嘛你可以自 CryptDrive 移除原有文件或是 昇級到付費版增加你的儲存容量。'; + out.pinLimitReachedAlert = out.updated_0_pinLimitReachedAlert; + out.pinLimitNotPinned = "你已達到容量使用上限
"+ + "這個檔案無法儲存到你的 CryptDrive."; + out.pinLimitDrive = "你已達到容量使用上限
" + + "你不能建立新的編輯檔案"; + out.importButtonTitle = '從電腦上傳滙入檔案'; + + out.exportButtonTitle = '將這個檔案滙出到電腦'; + out.exportPrompt = '你希望怎麼命名你的檔案?'; + + out.changeNamePrompt = '更換你的名稱(若留空白則會成為無名氏): '; + out.user_rename = "改變顯示名稱"; + out.user_displayName = "顯示名稱"; + out.user_accountName = "帳號名稱"; + + out.clickToEdit = "點擊以編輯"; + + out.forgetButtonTitle = '將這個檔案移置垃圾筒'; + out.forgetPrompt = '點擊 OK 將把這個檔案移置垃圾筒,確定要這樣做嗎'; + out.movedToTrash = '這個檔案已被移置垃圾筒
讀取我的雲端硬碟'; + + out.shareButton = '分享'; + out.shareSuccess = '複製連結到剪貼版'; + + out.newButton = '新'; + out.newButtonTitle = '建立新的工作檔案'; + + out.saveTemplateButton = "存成模版"; + out.saveTemplatePrompt = "為這個模版選一個標題"; + out.templateSaved = "模版已儲存!"; + out.selectTemplate = "選擇一個模版或是按 escape 跳出"; + + out.previewButtonTitle = "顯示或隱藏 Markdown 預覽模式"; + + out.presentButtonTitle = "輸入簡報模式"; + out.presentSuccess = '按 ESC 以退出簡報模式'; + + out.backgroundButtonTitle = '改變簡報的顏色背景'; + out.colorButtonTitle = '在簡報模式下改變文字顏色'; + + out.printButton = "列印 (enter)"; + out.printButtonTitle = "列印投影片或滙出成 PDF 檔案"; + out.printOptions = "版型選項"; + out.printSlideNumber = "顯示投影片號碼"; + out.printDate = "顯示日期"; + out.printTitle = "顯示檔案標題"; + out.printCSS = "自定風格規則 (CSS):"; + out.printTransition = "啟用轉場動畫"; + + out.slideOptionsTitle = "自定你的投影片"; + out.slideOptionsButton = "儲存 (enter)"; + + out.editShare = "編輯連結"; + out.editShareTitle = "複製所編輯的連結到剪貼版"; + out.editOpen = "在新分頁開啟連結編輯"; + out.editOpenTitle = "在新分頁開啟這個檔案為編輯模式"; + out.viewShare = "唯讀連結"; + out.viewShareTitle = "複製唯讀的連結到剪貼版"; + out.viewOpen = "在新分頁開啟唯讀連結"; + out.viewOpenTitle = "在新分頁開啟這個檔案為唯讀模式"; + + out.notifyJoined = "{0} 已加入此協作期間"; + out.notifyRenamed = "{0} 現在改名為 {1}"; + out.notifyLeft = "{0} 已離開了這個協作期間"; + + out.okButton = 'OK (enter)'; + + out.cancel = "取消"; + out.cancelButton = '取消 (esc)'; + + out.historyButton = "顯示文件歷史"; + out.history_next = "到下一個版本"; + out.history_prev = "到之前的版本"; + out.history_goTo = "到所選擇的版本"; + out.history_close = "回到"; + out.history_closeTitle = "關閉歷史記錄"; + out.history_restore = "重建"; + out.history_restoreTitle = "將此文件重建到所挑選的版本"; + out.history_restorePrompt = "確定要將這個展現的版本來取代現有版本嗎?"; + out.history_restoreDone = "文件已重建"; + out.history_version = "版本:"; + + // Polls + + out.poll_title = "零知識日期挑選"; + out.poll_subtitle = "零知識, 即時 排程"; + + out.poll_p_save = "你的設定會立即更新, 因此從不需要按鍵儲存或擔心遺失。"; + out.poll_p_encryption = "你所有幹入的資料都會予以加密,只有取得連結者才可以讀取它。即便是伺服器也不能看到你作了什麼變動。"; + + out.wizardLog = "點擊左上方的按鍵以回到你的調查"; + out.wizardTitle = "使用精靈來建立調查投票"; + out.wizardConfirm = "你真的要新增這些問題到你的調查中嗎?"; + + out.poll_publish_button = "發佈"; + out.poll_admin_button = "管理者"; + out.poll_create_user = "新增使用者"; + out.poll_create_option = "新增選項"; + out.poll_commit = "投入"; + + out.poll_closeWizardButton = "關閉協助精靈"; + out.poll_closeWizardButtonTitle = "關閉協助精靈"; + out.poll_wizardComputeButton = "計算最適化"; + out.poll_wizardClearButton = "清除表格"; + out.poll_wizardDescription = "透過輸入任何日期或時間分段,可自動建立一些選項"; + out.poll_wizardAddDateButton = "+ 日期"; + out.poll_wizardAddTimeButton = "+ 時間"; + + out.poll_optionPlaceholder = "選項"; + out.poll_userPlaceholder = "你的名稱"; + out.poll_removeOption = "確定要移除這個選項嗎?"; + out.poll_removeUser = "確定要移除這位使用者嗎?"; + + out.poll_titleHint = "標題"; + out.poll_descriptionHint = "請簡述這個調查目的,完成時使用「發佈鍵」。任何知道此調查連結者可以更改這裏的描述內容,但我們不鼓勵這麼做。."; + + // Canvas + out.canvas_clear = "清除"; + out.canvas_delete = "刪除所選"; + out.canvas_disable = "取消繪圖"; + out.canvas_enable = "啟動繪圖"; + out.canvas_width = "寛度"; + out.canvas_opacity = "透明度"; + + // File manager + + out.fm_rootName = "根目錄"; + out.fm_trashName = "垃圾桶"; + out.fm_unsortedName = "未整理的檔案"; + out.fm_filesDataName = "所有檔案"; + out.fm_templateName = "模版"; + out.fm_searchName = "搜尋"; + out.fm_searchPlaceholder = "搜尋..."; + out.fm_newButton = "新的"; + out.fm_newButtonTitle = "建立新工作檔案或資料夾"; + out.fm_newFolder = "新資料夾"; + out.fm_newFile = "新工作檔案"; + out.fm_folder = "資料夾"; + out.fm_folderName = "資料夾名稱"; + out.fm_numberOfFolders = "# 個資料夾"; + out.fm_numberOfFiles = "# 檔案"; + out.fm_fileName = "檔案名"; + out.fm_title = "標題"; + out.fm_type = "類型"; + out.fm_lastAccess = "上回使用"; + out.fm_creation = "創建"; + out.fm_forbidden = "禁止的行為"; + out.fm_originalPath = "原始路徑"; + out.fm_openParent = "顯示在目錄夾中"; + out.fm_noname = "無標題文件"; + out.fm_emptyTrashDialog = "確定要清理垃圾筒嗎?"; + out.fm_removeSeveralPermanentlyDialog = "確定要將這些 {0} 東西永自垃圾筒移除嗎?"; + out.fm_removePermanentlyDialog = "你確定要永久地移除這些項目嗎?"; + out.fm_removeSeveralDialog = "確定要將這些 {0} 東西移至垃圾筒嗎?"; + out.fm_removeDialog = "確定要將移動 {0} 至垃圾筒嗎?"; + out.fm_restoreDialog = "確定要重置 {0} 到它之前的位置嗎?"; + out.fm_unknownFolderError = "所選或上回訪問的目錄不再存在了,正開啟上層目錄中..."; + out.fm_contextMenuError = "無法在此元件下打開文本選單。如果這個問題一直發生,請試著重新載入此頁。"; + out.fm_selectError = "無法選取目標的要素。如果這個問題一直發生,請試著重新載入此頁。"; + out.fm_categoryError = "無法打開所選的類別,正在顯示根目錄。"; + out.fm_info_root = "在此建立任何巢狀目錄夾以便於整理分類你的檔案。"; + out.fm_info_unsorted = '包含所有你曾訪問過的檔案,其尚未被整理在 "根目錄" 或移到到"垃圾筒".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" + out.fm_info_template = '包含所有工作檔案已存成模版,便於讓你在建立新工作檔案時套用。'; + out.updated_0_fm_info_trash = '清空垃圾筒好讓 CryptDrive 多出一些空間'; + out.fm_info_trash = out.updated_0_fm_info_trash; + out.fm_info_allFiles = '包含在 "根目錄", "未整理的" 和 "垃圾筒" 裏的所有檔案。這裏你無法移動或移除檔案。'; // Same here + out.fm_info_anonymous = '你尚未登入,因此這些工作檔案可能會被刪除。 (了解原因). ' + + '註冊登入以便保留它們。'; + out.fm_alert_backupUrl = "這個雲端硬碟的備份連結
" + + "高度建議把自己的 IP 資訊保留成只有自己知道
" + + "萬一瀏覽器記憶被消除,你可以用它來接收所有的檔案。
" + + "任何知道此連結的人可以編輯或移除你檔案管理底下的所有檔案。
"; + out.fm_alert_anonymous = "嗨你好, 你目前正以匿名方式在使用 CryptPad , 這也沒問題,不過你的東西過一段時間沒動靜後,就會自動被刪除。 " + + "匿名的用戶我們也取消其進階功能,因為我們要明確地讓用戶知道,這裏 " + + '不是一個安全存放東西的地方。你可以 進一步了解 關於 ' + + '為何我們這樣作,以及為何你最好能夠註冊 以及 登錄使用。'; + out.fm_backup_title = '備份連結'; + out.fm_nameFile = '你想要如何來命名這個檔案呢?'; + out.fm_error_cantPin = "內部伺服器出錯,請重新載入本頁並再試一次。"; + // File - Context menu + out.fc_newfolder = "新資料夾"; + out.fc_rename = "重新命名"; + out.fc_open = "打開"; + out.fc_open_ro = "打開 (唯讀)"; + out.fc_delete = "刪除"; + out.fc_restore = "重置"; + out.fc_remove = "永久刪除"; + out.fc_empty = "清理垃圾筒"; + out.fc_prop = "Properties"; + out.fc_sizeInKilobytes = "容量大小 (Kilobytes)"; + // fileObject.js (logs) + out.fo_moveUnsortedError = "你不能移動資料夾到未整理的工作檔案清單"; + out.fo_existingNameError = "名稱已被使用,請選擇其它名稱"; + out.fo_moveFolderToChildError = "你不能移動資料夾到它的子資料夾底下"; + out.fo_unableToRestore = "無法將這個檔案重置到原始的位置。你可以試著將它移動到其它新位置。"; + out.fo_unavailableName = "在新位置裏同名的檔案或資料夾名稱已存在,請重新命名後再試看看。"; + + // login + out.login_login = "登入"; + out.login_makeAPad = '匿名地建立一個工作檔案'; + out.login_nologin = "瀏覽本地的工作檔案"; + out.login_register = "註冊"; + out.logoutButton = "登出"; + out.settingsButton = "設定"; + + out.login_username = "用戶名"; + out.login_password = "密碼"; + out.login_confirm = "確認你的密碼"; + out.login_remember = "記住我"; + + out.login_hashing = "散列你的密碼中,這要花上一點時間"; + + out.login_hello = 'Hello {0},'; // {0} is the username + out.login_helloNoName = 'Hello,'; + out.login_accessDrive = '取用你的磁碟'; + out.login_orNoLogin = '或'; + + out.login_noSuchUser = '無效的用戶名或密碼,請再試一次或重新註冊'; + out.login_invalUser = '要求用戶名'; + out.login_invalPass = '要求密碼'; + out.login_unhandledError = '發生了未預期的錯誤 :('; + + out.register_importRecent = "滙入檔案記錄 (建議)"; + out.register_acceptTerms = "我同意 服務條款"; + out.register_passwordsDontMatch = "密碼不相符!"; + out.register_mustAcceptTerms = "你必須同意我們的服務條款。"; + out.register_mustRememberPass = "如果你忘了密碼,我們也無法為你重置。因此務必自行好好記住! 請在勾選處勾選確認。"; + + out.register_header = "歡迎來到 CryptPad"; + out.register_explanation = [ + "

首先讓我們先了解幾件事

", + "" + ].join(''); + + out.register_writtenPassword = "我已記下了我的用戶名和密碼,請繼續"; + out.register_cancel = "回去"; + + out.register_warning = "零知識表示如果你遺失了密碼,我們也無法還原你的資料"; + + out.register_alreadyRegistered = "這名用戶己存在了,你要登入嗎?"; + + // Settings + out.settings_title = "設定"; + out.settings_save = "儲存"; + out.settings_backupTitle = "備份或重建你所有的資料"; + out.settings_backup = "備份"; + out.settings_restore = "重建"; + out.settings_resetTitle = "清除你的雲端硬碟"; + out.settings_reset = "從你的 CryptDrive 移除所有的檔案和資料夾"; + out.settings_resetPrompt = "這個動作會自你的雲端硬碟中移除所有工作檔案
"+ + "確定要繼續嗎?
" + + "輸入 “I love CryptPad” 來確認。"; + out.settings_resetDone = "你的目錄現已清空!"; + out.settings_resetError = "不正確的認證文字,你的 CryptDrive 並未更改。"; + out.settings_resetTips = "使用 CryptDrive 的竅門"; + out.settings_resetTipsButton = "在 CryptDrive 下重置可用的訣竅"; + out.settings_resetTipsDone = "所有的訣竅現在都可再次看到了。"; + + out.settings_importTitle = "滙入這個瀏覽器近期的工作檔案到我的 CryptDrive"; + out.settings_import = "滙入"; + out.settings_importConfirm = "確定要從這個瀏覽器滙入近期的工作檔案到你的 CryptDrive ?"; + out.settings_importDone = "滙入完成"; + + out.settings_userFeedbackHint1 = "CryptPad 會提供一些基本的反饋到伺服器,以讓我們知道如何改善用戶體驗。"; + out.settings_userFeedbackHint2 = "你的工作檔案內容絕不會被分享到伺服器"; + out.settings_userFeedback = "啟用用戶反饋功能"; + + out.settings_anonymous = "你尚未登入,在此瀏覽器上進行特別設定。"; + out.settings_publicSigningKey = "公開金鑰簽署"; + + out.settings_usage = "用法"; + out.settings_usageTitle = "查看所有置頂的工作檔案所佔的容量"; + out.settings_pinningNotAvailable = "工作檔案置頂功能只開放給已註冊用戶"; + out.settings_pinningError = "有點不對勁"; + out.settings_usageAmount = "你置頂的工作檔案佔了 {0}MB"; + + out.settings_logoutEverywhereTitle = "自所有地點登出"; + out.settings_logoutEverywhere = "自所有其它的網頁期間登出"; + out.settings_logoutEverywhereConfirm = "你確定嗎?你將需要登入到所有用到設置。"; + + out.upload_serverError = "伺服器出錯:本次無法上傳你的檔案"; + out.upload_uploadPending = "你欲上傳檔案正在傳輸中,要取消並上傳新檔案嗎?"; + out.upload_success = "你的檔案 ({0}) 已成功地上傳並放入到你的網路磁碟中。"; + out.upload_notEnoughSpace = "你的 CryptDrive 無足夠空間來存放這個檔案。"; + out.upload_tooLarge = "此檔案超過了上傳單一檔案可允許的容量上限。"; + out.upload_choose = "選擇一個檔案"; + out.upload_pending = "待處理"; + out.upload_cancelled = "已取消的"; + out.upload_name = "檔案名"; + out.upload_size = "大小"; + out.upload_progress = "進度"; + out.download_button = "解密 & 下載"; + + // general warnings + out.warn_notPinned = "這個工作檔案並不在任何人的 CryptDrive 裏,它將在 3 個月到期後刪除。 進一步了解..."; + + // index.html + + + //about.html + out.main_p2 = '本專案使用 CKEditor 視覺編輯器, CodeMirror, 以及 ChainPad 即時引擊。'; + out.main_howitworks_p1 = 'CryptPad 應用一種變體的 操作型變換 Operational transformation 演算法,它利用Nakamoto Blockchain來找到分散的共識, Nakamoto Blockchain 是一種建構當前流行的比特幣。這套演算法可避免需要一個中央的伺服器來解析操作型變換編輯衝突,而無須處理解析衝突,伺服器並不知道哪一個檔案被編輯。'; + + // contact.html + out.main_about_p2 = '若有任何問題和建議, 可以在tweet us, github提出問題, 或是來到 irc (irc.freenode.net)打聲招呼, 再或者 寄封電郵給我們.'; + + out.main_info = "

Collaborate in Confidence


利用共同享文件發嚮點子,透過 零知識 科技確保隱私安全; 對任何網路服務商都要加以提防。"; + + out.main_howitworks = '它如何運作'; + out.main_zeroKnowledge = '零知識'; + out.main_zeroKnowledge_p = "你不必相信我們所說的並不會 察看你的檔案, CryptPad 革命性的零知識技術讓我們 真的不能看到。 進一步了解在這裏,我們如何保護用戶的 隱私和安全。"; + out.main_writeItDown = '寫下它'; + out.main_writeItDown_p = "偉大的專案來自不起眼的小點子。記下靈感與點子的瞬間,因為你從不會知道哪個會帶來重大突破。"; + out.main_share = '分享連結, 分享工作檔案'; + out.main_share_p = "一起來發響想法點子: 在任何設備上,與朋友一起執行有效率的會議, 協作待辦清單與快速製作簡報。"; + out.main_organize = 'Get organized'; + out.main_organize_p = "利用 CryptPad 空間, 你可以保留看管重要的東西。資料夾讓你可以追踪專案和全盤了解事情的走向狀況。"; + out.tryIt = 'Try it out!'; + out.main_richText = '富文字編輯器'; + out.main_richText_p = '利用我們的即時零知識技術,集體協作地編輯富文本檔案 CkEditor 應用程式application.'; + out.main_code = '代碼編輯器'; + out.main_code_p = '利用我們的即時零知識技術,集體協作地編輯程式代碼 CodeMirror 應用程式。'; + out.main_slide = '投影片編輯器'; + out.main_slide_p = '使用 Markdown 語法來建立投影片,並利用瀏覽器來展示投影片。'; + out.main_poll = '調查'; + out.main_poll_p = '規劃會議或活動,或是為問題舉行投最佳方案的投票。'; + out.main_drive = 'CryptDrive'; + + out.footer_applications = "應用程式"; + out.footer_contact = "聯繫"; + out.footer_aboutUs = "關於 Cryptpad"; + + out.about = "關於"; + out.privacy = "隱私"; + out.contact = "聯繫"; + out.terms = "服務條款"; + out.blog = "Blog"; + + // privacy.html + + out.policy_title = 'CryptPad 隱私政策'; + out.policy_whatweknow = '我們會知道哪些關於你的資料'; + out.policy_whatweknow_p1 = '作為一個網頁上的應用程式, CryptPad 可以接取 HTTP 協議所曝露的元數據。 這包括你的 IP 地址、各式其它的 HTTP 標頭,其用於識別你特定的瀏覽器。 你可以訪問 WhatIsMyBrowser.com這個網站,知道你的瀏覽器分享了哪些資訊。'; + out.policy_whatweknow_p2 = '我們使用 Kibana, 它是一個開源的流量數據分析平台, 以更了解用戶。Kibana 讓我們知道你是如何地發現 CryptPad, 是透過直接接入、攑搜尋引擊或是其它網站的介紹如 Reddit 和 Twitter。'; + out.policy_howweuse = '我們如何利用我們知道的東西'; + out.policy_howweuse_p1 = '我們利用這些資訊評估過去成功的效果,以更佳地決定如何推廣 CryptPad。有關你地理位置的資訊讓我們知道是否該提供英語之外的語言版本支援'; + out.policy_howweuse_p2 = "有關你的瀏覽器資訊 (是桌面還是手機操作系統) 有助於讓我們決定要優先哪些功能改善。我們開發團隊人很少,我們試著挑選盡可能地提昇更多用戶的使用體驗。"; + out.policy_whatwetell = '我們可以告訴別人關於你的哪些資料'; + out.policy_whatwetell_p1 = '我們不會給第三人我們所收集的資訊,除非被依法要求配合。'; + out.policy_links = '其它網站連結'; + out.policy_links_p1 = '本站含有其它網站的連結,包括其它組織的産品。我們無法對這些隱私實踐或任何本站以外的內容負責。一般而言,連到外站的連結會另啟新視窗,以明確讓你知道已離開了CryptPad.fr.'; + out.policy_ads = '廣告'; + out.policy_ads_p1 = '我們不會放置任何線上廣告,但會提供一些資助我們研究的機構與團體的網址連結'; + out.policy_choices = '你有的選擇'; + out.policy_choices_open = '我們的代碼是開放的,你可以選擇自行在自己的機器上來架設自己的 CryptPad.'; + out.policy_choices_vpn = '如果你要使用我們架設的服務, 但不希望曝露自己的 IP 地址, 你可以利用Tor 瀏覽器套件來保護隱藏 IP 地址, 或是使用 VPN。'; + out.policy_choices_ads = '如果你只是想要封鎖我們的數據分析器, 你可以使用廣告封鎖工具如 Privacy Badger.'; + + // terms.html + + out.tos_title = "CryptPad 服務條款"; + out.tos_legal = "請不要惡意、濫用或從事非法活動。"; + out.tos_availability = "希望你覺得我們的産品與服務對你有所幫助, 但我們並不能一直百分百保證它的表現穩定與可得性。請記得定期滙出你的資料。"; + out.tos_e2ee = "CryptPad 的內容可以被任何猜出或取得工作檔案分段識別碼的人讀取與修改。我們建議你使用端對端加密 (e2ee) 訊息技術來分享工作檔案連結 以及假設如果一旦連結外漏不會背上任何責任。"; + out.tos_logs = "你的瀏覽器提供給伺服器的元數據,可能會因為維護本服務之效能而被收集記錄。"; + out.tos_3rdparties = "除非法令要求,我們不會提供任何個人資料給第三方。"; + + // BottomBar.html + + out.bottom_france = 'Made with love in France'; + out.bottom_support = 'An XWiki SAS Labs Project with the support of OpenPaaS-ng'; + + // Header.html + + out.header_france = 'With love from France by XWiki SAS'; + + out.header_support = ' OpenPaaS-ng'; + out.header_logoTitle = '回到主頁'; + + // Initial states + + out.initialState = [ + '

', + '這是 CryptPad, 零知識即時協作編輯平台,當你輸入時一切已即存好。', + '
', + '分享這個工作檔案的網址連結給友人或是使用、  分享  按鈕分享唯讀的連結 其只能看不能編寫。', + '

', + + '

', + '來吧, 開始打字輸入吧...', + '

', + '

 

' + ].join(''); + + out.codeInitialState = [ + '# CryptPad 零知識即時協作代碼編輯平台\n', + '\n', + '* 你所輸入的東西會予以加密,僅有知道此網頁連結者可以接取這份文件。\n', + '* 你可以在右上角選擇欲編寫的程式語言以及樣版配色風格。' + ].join(''); + + out.slideInitialState = [ + '# CryptSlide\n', + '* 它是零知識即時協作編輯平台。\n', + '* 你所輸入的東西會予以加密,僅有知道此網頁連結者可以接取這份文件。\n', + '* 即便是本站伺服器也不知道你輸入了什麼內容。\n', + '* 你在這裏看到的、你在這裏聽到的、當你離開本站時,讓它就留在這裏吧。\n', + '\n', + '---', + '\n', + '# 如何使用\n', + '1. 使用 markdown 語法來寫下你的投影片內容\n', + ' - 進一步學習 markdown 語法 [here](http://www.markdowntutorial.com/)\n', + '2. 利用 --- 來區隔不同的投影片\n', + '3. 點擊下方 "Play" 鍵來查看成果', + ' - 你的投影片會即時更新' + ].join(''); + + // Readme + + out.driveReadmeTitle = "什麼是 CryptDrive?"; + out.readme_welcome = "歡迎來到 CryptPad !"; + out.readme_p1 = "歡迎來到 CryptPad, 這裏你可以獨自作個人筆記或是和別人共享協作。"; + out.readme_p2 = "這個工作檔案可以讓你快速地了解如何使用 CryptPad 作筆記,有效地整理管理文件工作檔案。"; + out.readme_cat1 = "認識如何使用 CryptDrive"; + out.readme_cat1_l1 = "建立一個工作檔案: 在 CryptDrive 底下, 點擊 {0} 然後 {1} 這樣就可以建立一個新的工作檔案。"; // 0: New, 1: Rich Text + out.readme_cat1_l2 = "從 CryptDrive 開啟工作檔案: 雙擊工作檔案的圖示來開啟它。"; + out.readme_cat1_l3 = "分類你的工作檔案:登入之後,每一個你能接取使用的工作檔案會顯示在你雲端硬碟中的 {0} 部份。"; // 0: Unsorted files + out.readme_cat1_l3_l1 = "你可以點擊或是拉曳檔案到雲端硬碟 {0} 區,新增資料夾。"; // 0: Documents + out.readme_cat1_l3_l2 = "記得試著點擊圖示,以顯示更多的選項功能。"; + out.readme_cat1_l4 = "把舊的工作檔案放到垃圾筒:點擊或是拉曳檔案到 {0} 如同把它們拉到文件目錄夾一樣的方法。"; // 0: Trash + out.readme_cat2 = "像個專業人士來編寫你的工作檔案"; + out.edit = "編輯"; + out.view = "檢視"; + out.readme_cat2_l1 = "在工作檔案下的 {0} 按鍵可讓其它的協作者接取 {1} 或是 {2} 工作檔案"; // 0: Share, 1: edit, 2: view + out.readme_cat2_l2 = "若要更改工作檔案的名稱,只要點擊右上的鉛筆圖示即可"; + out.readme_cat3 = "發現其它的 CryptPad 應用"; + out.readme_cat3_l1 = "使用 CryptPad 代碼編輯器,你可以和其它人協作各種程式碼,如 Javascript、 markdown、 HTML 等等。"; + out.readme_cat3_l2 = "使用 CryptPad 投影片編輯功能,你可以使用 Markdown 快速製作簡報檔。"; + out.readme_cat3_l3 = "利用 CryptPoll 你可以快速作個線上調查,尤其是調查每個人有空的會議時間。"; + + // Tips + out.tips = {}; + out.tips.lag = "右上角的綠色圖標顯示你連線至 CryptPad 伺服器的連線品質。"; + out.tips.shortcuts = "`ctrl+b`, `ctrl+i` 和 `ctrl+u` 分別是粗體字、斜體、與加底線用法的快速鍵。"; + out.tips.indent = "要使用數字以及符號列表, 可使用 tab 或 shift+tab 快速地增加或滅少縮排指令。"; + out.tips.title = "點擊正上方來設定工作檔案的標題。"; + out.tips.store = "每一回你造訪一個工作檔案, 如果是登入狀態,則這些檔案會自動儲存到你的 CryptDrive."; + out.tips.marker = "在格式下拉選單中使用 \"marker\" 可以標注反亮文字."; + + out.feedback_about = "如果你讀了這裏,也許會好奇為何當你執行某些動作時 CryptPad 會請求網頁資訊。"; + out.feedback_privacy = "我們注重你的隱私,同時也要讓 CryptPad 容易使用。我們利用這個檔案來了解哪一種介面設計為用戶所重視,透過它來請求特別的功能參數。"; + out.feedback_optout = "如果欲退出客戶資料收集, 請到 用戶設定頁, 可以找到勾選項目來啟用或關閉用戶回饋功能。"; + + return out; +}); + diff --git a/ARCHITECTURE.md b/docs/ARCHITECTURE.md similarity index 100% rename from ARCHITECTURE.md rename to docs/ARCHITECTURE.md diff --git a/cryptpad-docker.md b/docs/cryptpad-docker.md similarity index 100% rename from cryptpad-docker.md rename to docs/cryptpad-docker.md diff --git a/example.nginx.conf b/docs/example.nginx.conf similarity index 89% rename from example.nginx.conf rename to docs/example.nginx.conf index fcb8b7435..37cb0da60 100644 --- a/example.nginx.conf +++ b/docs/example.nginx.conf @@ -1,3 +1,9 @@ +# This file is included strictly as an example of how Nginx can be configured +# to work with CryptPad. This example WILL NOT WORK AS IS. For best results, +# compare the sections of this configuration file against a working CryptPad +# installation (http server by the Nodejs process). If you are using CryptPad +# in production, contact sales@cryptpad.fr + server { listen 443 ssl http2; diff --git a/package.json b/package.json index e75ec7b32..9454404ac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "1.7.0", + "version": "1.8.0", "dependencies": { "chainpad-server": "^1.0.1", "express": "~4.10.1", @@ -18,7 +18,7 @@ "scripts": { "lint": "jshint --config .jshintrc --exclude-path .jshintignore .", "test": "node TestSelenium.js", - "style": "lessc ./customize.dist/src/less/cryptpad.less > ./customize.dist/main.css && lessc ./customize.dist/src/less/toolbar.less > ./customize.dist/toolbar.css && lessc ./www/drive/file.less > ./www/drive/file.css && lessc ./www/settings/main.less > ./www/settings/main.css && lessc ./www/slide/slide.less > ./www/slide/slide.css && lessc ./www/whiteboard/whiteboard.less > ./www/whiteboard/whiteboard.css && lessc ./www/poll/poll.less > ./www/poll/poll.css && lessc ./www/file/file.less > ./www/file/file.css", + "style": "lessc ./customize.dist/src/less/cryptpad.less > ./customize.dist/main.css && lessc ./customize.dist/src/less/toolbar.less > ./customize.dist/toolbar.css && lessc ./www/drive/file.less > ./www/drive/file.css && lessc ./www/settings/main.less > ./www/settings/main.css && lessc ./www/slide/slide.less > ./www/slide/slide.css && lessc ./www/whiteboard/whiteboard.less > ./www/whiteboard/whiteboard.css && lessc ./www/poll/poll.less > ./www/poll/poll.css && lessc ./www/file/file.less > ./www/file/file.css && lessc ./www/code/code.less > ./www/code/code.css", "template": "cd customize.dist/src && node build.js" } } diff --git a/pinneddata.js b/pinneddata.js index 0bf9be75f..8dbbc7fec 100644 --- a/pinneddata.js +++ b/pinneddata.js @@ -1,4 +1,4 @@ -/* jshint esversion: 6 */ +/* jshint esversion: 6, node: true */ const Fs = require('fs'); const Semaphore = require('saferphore'); const nThen = require('nthen'); @@ -26,14 +26,14 @@ const hashesFromPinFile = (pinFile, fileName) => { return Object.keys(pins); }; -const sizeForHashes = (hashes, dsFileSizes) => { +const sizeForHashes = (hashes, dsFileStats) => { let sum = 0; hashes.forEach((h) => { - const s = dsFileSizes[h]; + const s = dsFileStats[h]; if (typeof(s) !== 'number') { //console.log('missing ' + h + ' ' + typeof(s)); } else { - sum += s; + sum += s.size; } }); return sum; @@ -43,8 +43,9 @@ const sema = Semaphore.create(20); let dirList; const fileList = []; -const dsFileSizes = {}; +const dsFileStats = {}; const out = []; +const pinned = {}; nThen((waitFor) => { Fs.readdir('./datastore', waitFor((err, list) => { @@ -65,7 +66,7 @@ nThen((waitFor) => { sema.take((returnAfter) => { Fs.stat(f, waitFor(returnAfter((err, st) => { if (err) { throw err; } - dsFileSizes[f.replace(/^.*\/([^\/]*)\.ndjson$/, (all, a) => (a))] = st.size; + dsFileStats[f.replace(/^.*\/([^\/]*)\.ndjson$/, (all, a) => (a))] = st; }))); }); }); @@ -90,12 +91,25 @@ nThen((waitFor) => { Fs.readFile(f, waitFor(returnAfter((err, content) => { if (err) { throw err; } const hashes = hashesFromPinFile(content.toString('utf8'), f); - const size = sizeForHashes(hashes, dsFileSizes); - out.push([f, Math.floor(size / (1024 * 1024))]); + const size = sizeForHashes(hashes, dsFileStats); + if (process.argv.indexOf('--unpinned') > -1) { + hashes.forEach((x) => { pinned[x] = 1; }); + } else { + out.push([f, Math.floor(size / (1024 * 1024))]); + } }))); }); }); }).nThen(() => { - out.sort((a,b) => (a[1] - b[1])); - out.forEach((x) => { console.log(x[0] + ' ' + x[1] + ' MB'); }); + if (process.argv.indexOf('--unpinned') > -1) { + Object.keys(dsFileStats).forEach((f) => { + if (!(f in pinned)) { + console.log("./datastore/" + f.slice(0,2) + "/" + f + ".ndjson " + + dsFileStats[f].size + " " + (+dsFileStats[f].mtime)); + } + }); + } else { + out.sort((a,b) => (a[1] - b[1])); + out.forEach((x) => { console.log(x[0] + ' ' + x[1] + ' MB'); }); + } }); diff --git a/rpc.js b/rpc.js index 0257c498f..cfb6d7772 100644 --- a/rpc.js +++ b/rpc.js @@ -1,4 +1,5 @@ /*@flow*/ +/*jshint esversion: 6 */ /* Use Nacl for checking signatures of messages */ var Nacl = require("tweetnacl"); @@ -8,15 +9,17 @@ var Nacl = require("tweetnacl"); var Fs = require("fs"); var Path = require("path"); var Https = require("https"); +const Package = require('./package.json'); var RPC = module.exports; var Store = require("./storage/file"); var DEFAULT_LIMIT = 50 * 1024 * 1024; +var SESSION_EXPIRATION_TIME = 60 * 1000; var isValidId = function (chan) { - return /^[a-fA-F0-9]/.test(chan) || + return chan && chan.length && /^[a-fA-F0-9]/.test(chan) || [32, 48].indexOf(chan.length) !== -1; }; @@ -75,13 +78,14 @@ var parseCookie = function (cookie) { }; var escapeKeyCharacters = function (key) { - return key.replace(/\//g, '-'); + return key && key.replace && key.replace(/\//g, '-'); }; var unescapeKeyCharacters = function (key) { return key.replace(/\-/g, '/'); }; +// TODO Rename to getSession ? var beginSession = function (Sessions, key) { var safeKey = escapeKeyCharacters(key); if (Sessions[safeKey]) { @@ -216,6 +220,7 @@ var loadUserPins = function (Env, publicKey, cb) { var parsed; try { parsed = JSON.parse(msg); + session.hasPinned = true; switch (parsed[0]) { case 'PIN': @@ -272,7 +277,11 @@ var getUploadSize = function (Env, channel, cb) { } Fs.stat(path, function (err, stats) { - if (err) { return void cb(err); } + if (err) { + // if a file was deleted, its size is 0 bytes + if (err.code === 'ENOENT') { return cb(void 0, 0); } + return void cb(err); + } cb(void 0, stats.size); }); }; @@ -371,6 +380,10 @@ var getHash = function (Env, publicKey, cb) { // To each key is associated an object containing the 'limit' value and a 'note' explaining that limit var limits = {}; var updateLimits = function (config, publicKey, cb) { + if (config.adminEmail === false) { + if (config.allowSubscriptions === false) { return; } + throw new Error("allowSubscriptions must be false if adminEmail is false"); + } if (typeof cb !== "function") { cb = function () {}; } var defaultLimit = typeof(config.defaultStorageLimit) === 'number'? @@ -382,8 +395,10 @@ var updateLimits = function (config, publicKey, cb) { } var body = JSON.stringify({ - domain: config.domain, - subdomain: config.subdomain + domain: config.myDomain, + subdomain: config.mySubdomain, + adminEmail: config.adminEmail, + version: Package.version }); var options = { host: 'accounts.cryptpad.fr', @@ -561,7 +576,16 @@ var resetUserPins = function (Env, publicKey, channelList, cb) { console.error(e); return void cb(e); } - if (pinSize > free) { return void(cb('E_OVER_LIMIT')); } + + /* we want to let people pin, even if they are over their limit, + but they should only be able to do this once. + + This prevents data loss in the case that someone registers, but + does not have enough free space to pin their migrated data. + + They will not be able to pin additional pads until they upgrade + or delete enough files to go back under their limit. */ + if (pinSize > free && session.hasPinned) { return void(cb('E_OVER_LIMIT')); } pinStore.message(publicKey, JSON.stringify(['RESET', channelList]), function (e) { if (e) { return void cb(e); } @@ -617,6 +641,7 @@ var makeFileStream = function (root, id, cb) { var stream = Fs.createWriteStream(full, { flags: 'a', encoding: 'binary', + highWaterMark: Math.pow(2, 16), }); stream.on('open', function () { cb(void 0, stream); @@ -629,12 +654,15 @@ var makeFileStream = function (root, id, cb) { var upload = function (Env, publicKey, content, cb) { var paths = Env.paths; - var dec = new Buffer(Nacl.util.decodeBase64(content)); // jshint ignore:line + var dec; + try { dec = Buffer.from(content, 'base64'); } + catch (e) { return void cb(e); } var len = dec.length; var session = beginSession(Env.Sessions, publicKey); - if (typeof(session.currentUploadSize) !== 'number') { + if (typeof(session.currentUploadSize) !== 'number' || + typeof(session.currentUploadSize) !== 'number') { // improperly initialized... maybe they didn't check before uploading? // reject it, just in case return cb('NOT_READY'); @@ -662,6 +690,12 @@ var upload = function (Env, publicKey, content, cb) { var upload_cancel = function (Env, publicKey, cb) { var paths = Env.paths; + + var session = beginSession(Env.Sessions, publicKey); + delete session.currentUploadSize; + delete session.pendingUploadSize; + if (session.blobstage) { session.blobstage.close(); } + var path = makeFilePath(paths.staging, publicKey); if (!path) { console.log(paths.staging, publicKey); @@ -777,6 +811,24 @@ var upload_status = function (Env, publicKey, filesize, cb) { }); }; +var isAuthenticatedCall = function (call) { + return [ + 'COOKIE', + 'RESET', + 'PIN', + 'UNPIN', + 'GET_HASH', + 'GET_TOTAL_SIZE', + 'GET_FILE_SIZE', + 'UPDATE_LIMITS', + 'GET_LIMIT', + 'GET_MULTIPLE_FILE_SIZE', + //'UPLOAD', + 'UPLOAD_COMPLETE', + 'UPLOAD_CANCEL', + ].indexOf(call) !== -1; +}; + /*::const ConfigType = require('./config.example.js');*/ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)=>void*/) { // load pin-store... @@ -784,7 +836,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) var warn = function (e, output) { if (e && !config.suppressRPCErrors) { - console.error('[' + e + ']', output); + console.error(new Date().toISOString() + ' [' + e + ']', output); } }; @@ -832,7 +884,6 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) beginSession(Sessions, publicKey); var cookie = msg[0]; - if (!isValidCookie(Sessions, publicKey, cookie)) { // no cookie is fine if the RPC is to get a cookie if (msg[1] !== 'COOKIE') { @@ -846,8 +897,10 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY'); } - if (checkSignature(serialized, signature, publicKey) !== true) { - return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY"); + if (isAuthenticatedCall(msg[1])) { + if (checkSignature(serialized, signature, publicKey) !== true) { + return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY"); + } } var safeKey = escapeKeyCharacters(publicKey); @@ -855,6 +908,9 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) public key which you provided. We can safely modify the state for that key + + OR it's an unauthenticated call, which must not modify the state + for that key in a meaningful way. */ // discard validated cookie from message @@ -908,8 +964,8 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) Respond(e, size); }); case 'GET_FILE_SIZE': - return void getFileSize(Env, msg[2], function (e, size) { - warn(e, msg[2]); + return void getFileSize(Env, msg[1], function (e, size) { + warn(e, msg[1]); Respond(e, size); }); case 'UPDATE_LIMITS': @@ -1017,7 +1073,7 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) // expire old sessions once per minute setInterval(function () { expireSessions(Sessions); - }, 60000); + }, SESSION_EXPIRATION_TIME); }); }); }); diff --git a/server.js b/server.js index 037a8adeb..289184d8a 100644 --- a/server.js +++ b/server.js @@ -34,6 +34,11 @@ var setHeaders = (function () { const headers = clone(config.httpHeaders); if (config.contentSecurity) { headers['Content-Security-Policy'] = clone(config.contentSecurity); + if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) { + // backward compat for those who do not merge the new version of the config + // when updating. This prevents endless spinner if someone clicks donate. + headers['Content-Security-Policy'] += "frame-ancestors 'self' accounts.cryptpad.fr;"; + } } const padHeaders = clone(headers); if (config.padContentSecurity) { @@ -121,6 +126,9 @@ app.get('/api/config', function(req, res){ waitSeconds: 60, urlArgs: 'ver=' + Package.version + (DEV_MODE? '-' + (+new Date()): ''), }, + removeDonateButton: (config.removeDonateButton === true), + allowSubscriptions: (config.allowSubscriptions === true), + websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath, websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' + websocketPort + '/cryptpad_websocket', diff --git a/www/assert/main.js b/www/assert/main.js index 9c7c1f413..7124f58d9 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -4,8 +4,9 @@ define([ '/bower_components/textpatcher/TextPatcher.amd.js', 'json.sortify', '/common/cryptpad-common.js', - '/drive/tests.js' -], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive) { + '/drive/tests.js', + '/common/test.js' +], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test) { window.Hyperjson = Hyperjson; window.TextPatcher = TextPatcher; window.Sortify = Sortify; @@ -29,6 +30,7 @@ define([ ASSERTS.forEach(function (f, index) { f(function (err) { + console.log("test " + index); done(err, index); }, index); }); @@ -271,6 +273,12 @@ The test returned: var $report = $('.report'); $report.addClass(failed?'failure':'success'); + + if (failed) { + Test.failed(); + } else { + Test.passed(); + } }); }); diff --git a/www/auth/main.js b/www/auth/main.js index 488165911..577e1e966 100644 --- a/www/auth/main.js +++ b/www/auth/main.js @@ -1,8 +1,9 @@ define([ 'jquery', '/common/cryptpad-common.js', + '/common/test.js', '/bower_components/tweetnacl/nacl-fast.min.js' -], function ($, Cryptpad) { +], function ($, Cryptpad, Test) { var Nacl = window.nacl; var signMsg = function (msg, privKey) { @@ -18,8 +19,16 @@ define([ /^http(s)?:\/\/localhost\:/ ]; + // Safari is weird about localStorage in iframes but seems to let sessionStorage slide. + localStorage.User_hash = localStorage.User_hash || sessionStorage.User_hash; + Cryptpad.ready(function () { console.log('IFRAME READY'); + Test(function () { + // This is only here to maybe trigger an error. + window.drive = Cryptpad.getStore().getProxy().proxy['drive']; + Test.passed(); + }); $(window).on("message", function (jqe) { var evt = jqe.originalEvent; var data = JSON.parse(evt.data); diff --git a/www/code/code.css b/www/code/code.css new file mode 100644 index 000000000..589544493 --- /dev/null +++ b/www/code/code.css @@ -0,0 +1,74 @@ +html, +body { + height: 100%; + width: 100%; + padding: 0px; + margin: 0px; + overflow: hidden; + box-sizing: border-box; + position: relative; +} +body { + display: flex; + flex-flow: column; + max-height: 100%; + min-height: auto; +} +.CodeMirror { + display: inline-block; + height: 100%; + width: 50%; + transition: width 500ms, min-width 500ms, max-width 500ms; + min-width: 20%; + max-width: 80%; + resize: horizontal; +} +.CodeMirror.fullPage { + min-width: 100%; + max-width: 100%; + resize: none; +} +.CodeMirror-focused .cm-matchhighlight { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==); + background-position: bottom; + background-repeat: repeat-x; +} +#editorContainer { + flex: 1; + display: flex; + flex-flow: row; + height: 100%; + overflow: hidden; +} +#previewContainer { + flex: 1; + padding: 5px 20px; + overflow: auto; + display: inline-block; + height: 100%; + border-left: 1px solid black; + box-sizing: border-box; + font-family: Calibri, Ubuntu, sans-serif; + word-wrap: break-word; +} +#preview { + max-width: 40vw; + margin: auto; +} +#preview table { + border-collapse: collapse; +} +#preview table tr th { + border: 3px solid black; + padding: 15px; +} +@media (max-width: 600px) { + .CodeMirror { + flex: 1; + max-width: 100%; + resize: none; + } + #previewContainer { + display: none !important; + } +} diff --git a/www/code/code.less b/www/code/code.less new file mode 100644 index 000000000..04e3ab615 --- /dev/null +++ b/www/code/code.less @@ -0,0 +1,84 @@ +@import "../../customize.dist/src/less/variables.less"; +@import "../../customize.dist/src/less/mixins.less"; + +html, body{ + height: 100%; + width: 100%; + padding: 0px; + margin: 0px; + overflow: hidden; + box-sizing: border-box; + position: relative; +} +body { + display: flex; + flex-flow: column; + max-height: 100%; + min-height: auto; +} + +@slideTime: 500ms; +.CodeMirror { + display: inline-block; + height: 100%; + width: 50%; + transition: width @slideTime, min-width @slideTime, max-width @slideTime; + min-width: 20%; + max-width: 80%; + resize: horizontal; +} +.CodeMirror.fullPage { + min-width: 100%; + max-width: 100%; + resize: none; +} +.CodeMirror-focused .cm-matchhighlight { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==); + background-position: bottom; + background-repeat: repeat-x; +} +#editorContainer { + flex: 1; + display: flex; + flex-flow: row; + height: 100%; + overflow: hidden; +} +#previewContainer { + flex: 1; + padding: 5px 20px; + overflow: auto; + display: inline-block; + height: 100%; + border-left: 1px solid black; + box-sizing: border-box; + font-family: Calibri,Ubuntu,sans-serif; + word-wrap: break-word; +} + +#preview { + max-width: 40vw; + margin: auto; + + table { + border-collapse: collapse; + tr { + th { + border: 3px solid black; + padding: 15px; + } + } + } +} + +@media (max-width: @media-medium-screen) { + .CodeMirror { + flex: 1; + max-width: 100%; + resize: none; + } + #previewContainer { + display: none !important; + } +} + diff --git a/www/code/index.html b/www/code/index.html index 448020d26..4fa11fc33 100644 --- a/www/code/index.html +++ b/www/code/index.html @@ -5,6 +5,7 @@ + + @@ -31,70 +32,6 @@ -
diff --git a/www/code/main.js b/www/code/main.js index 660473ffd..5e9983fe6 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -53,6 +53,7 @@ define([ var andThen = function (CMeditor) { var CodeMirror = Cryptpad.createCodemirror(CMeditor, ifrw, Cryptpad); + $iframe.find('.CodeMirror').addClass('fullPage'); editor = CodeMirror.editor; var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox'); @@ -110,8 +111,16 @@ define([ return stringify(obj); }; + var forceDrawPreview = function () { + try { + DiffMd.apply(DiffMd.render(editor.getValue()), $preview); + } catch (e) { console.error(e); } + }; + var drawPreview = Cryptpad.throttle(function () { - DiffMd.apply(DiffMd.render(editor.getValue()), $preview); + if (CodeMirror.highlightMode !== 'markdown') { return; } + if (!$previewContainer.is(':visible')) { return; } + forceDrawPreview(); }, 150); var onLocal = config.onLocal = function () { @@ -137,8 +146,13 @@ define([ var $codeMirror = $iframe.find('.CodeMirror'); if (mode === "markdown") { APP.$previewButton.show(); - $previewContainer.show(); - $codeMirror.removeClass('fullPage'); + Cryptpad.getPadAttribute('previewMode', function (e, data) { + if (e) { return void console.error(e); } + if (data !== false) { + $previewContainer.show(); + $codeMirror.removeClass('fullPage'); + } + }); return; } APP.$previewButton.hide(); @@ -155,7 +169,7 @@ define([ Metadata = Cryptpad.createMetadata(UserList, Title); var configTb = { - displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], + displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'], userList: UserList.getToolbarConfig(), share: { secret: secret, @@ -183,8 +197,8 @@ define([ /* add a history button */ var histConfig = { - onLocal: config.onLocal(), - onRemote: config.onRemote(), + onLocal: config.onLocal, + onRemote: config.onRemote, setHistory: setHistory, applyVal: function (val) { var remoteDoc = JSON.parse(val || '{}').content; @@ -235,9 +249,16 @@ define([ } $previewContainer.toggle(); if ($previewContainer.is(':visible')) { + forceDrawPreview(); $codeMirror.removeClass('fullPage'); + Cryptpad.setPadAttribute('previewMode', true, function (e) { + if (e) { return console.log(e); } + }); } else { $codeMirror.addClass('fullPage'); + Cryptpad.setPadAttribute('previewMode', false, function (e) { + if (e) { return console.log(e); } + }); } }); $rightside.append($previewButton); @@ -251,6 +272,7 @@ define([ CodeMirror.configureTheme(); } + // set the hash if (!readOnly) { Cryptpad.replaceHash(editHash); } }; @@ -302,6 +324,13 @@ define([ Title.updateTitle(Cryptpad.initialName); } + Cryptpad.getPadAttribute('previewMode', function (e, data) { + if (e) { return void console.error(e); } + if (data === false && APP.$previewButton) { + APP.$previewButton.click(); + } + }); + Cryptpad.removeLoadingScreen(); setEditable(true); initializing = false; diff --git a/www/common/boot2.js b/www/common/boot2.js index d894191e8..76ce9bcf9 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -7,7 +7,9 @@ define([], function () { // jquery declares itself as literally "jquery" so it cannot be pulled by path :( "jquery": "/bower_components/jquery/dist/jquery.min", // json.sortify same - "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify" + "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify", + "pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf", + "pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker" } }); diff --git a/www/common/common-interface.js b/www/common/common-interface.js index b2b5dad10..5d1e01cc8 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -194,10 +194,17 @@ define([ }; UI.removeLoadingScreen = function (cb) { $('#' + LOADING).fadeOut(750, cb); - $('#loadingTip').css('top', ''); - window.setTimeout(function () { - $('#loadingTip').fadeOut(750); - }, 3000); + var $tip = $('#loadingTip').css('top', '') + // loading.less sets transition-delay: $wait-time + // and transition: opacity $fadeout-time + .css({ + 'opacity': 0, + 'pointer-events': 'none', + }); + setTimeout(function () { + $tip.remove(); + }, 3750); + // jquery.fadeout can get stuck }; UI.errorLoadingScreen = function (error, transparent) { if (!$('#' + LOADING).is(':visible')) { UI.addLoadingScreen(undefined, true); } diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 50e99547b..b5707639d 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -25,7 +25,10 @@ define([ */ var common = window.Cryptpad = { Messages: Messages, - Clipboard: Clipboard + Clipboard: Clipboard, + donateURL: 'https://accounts.cryptpad.fr/#/donate?on=' + window.location.hostname, + upgradeURL: 'https://accounts.cryptpad.fr/#/?on=' + window.location.hostname, + account: {}, }; // constants @@ -216,6 +219,7 @@ define([ userNameKey, userHashKey, 'loginToken', + 'plan', ].forEach(function (k) { sessionStorage.removeItem(k); localStorage.removeItem(k); @@ -244,6 +248,11 @@ define([ var getUserHash = common.getUserHash = function () { var hash = localStorage[userHashKey]; + if (['undefined', 'undefined/'].indexOf(hash) !== -1) { + localStorage.removeItem(userHashKey); + return; + } + if (hash) { var sHash = common.serializeHash(hash); if (sHash !== hash) { localStorage[userHashKey] = sHash; } @@ -575,7 +584,7 @@ define([ var data = makePad(href, name); getStore().pushData(data, function (e, id) { if (e) { - if (e === 'E_OVER_LIMIT' && AppConfig.enablePinLimit) { + if (e === 'E_OVER_LIMIT') { common.alert(Messages.pinLimitNotPinned, null, true); return; } @@ -741,7 +750,7 @@ define([ }; common.isOverPinLimit = function (cb) { - if (!common.isLoggedIn() || !AppConfig.enablePinLimit) { return void cb(null, false); } + if (!common.isLoggedIn()) { return void cb(null, false); } var usage; var andThen = function (e, limit, plan) { if (e) { return void cb(e); } @@ -775,7 +784,6 @@ define([ }; var LIMIT_REFRESH_RATE = 30000; // milliseconds - var limitReachedDisplayed = false; common.createUsageBar = function (cb, alwaysDisplayUpgrade) { var todo = function (err, state, data) { var $container = $('', {'class':'limit-container'}); @@ -797,7 +805,10 @@ define([ var width = Math.floor(Math.min(quota, 1)*200); // the bar is 200px width var $usage = $('', {'class': 'usage'}).css('width', width+'px'); - if ((quota >= 0.8 || alwaysDisplayUpgrade) && data.plan !== "power") { + if (Config.noSubscriptionButton !== true && + (quota >= 0.8 || alwaysDisplayUpgrade) && + data.plan !== "power") + { var origin = encodeURIComponent(window.location.hostname); var $upgradeLink = $('', { href: "https://accounts.cryptpad.fr/#!on=" + origin, @@ -823,13 +834,7 @@ define([ if (quota < 0.8) { $usage.addClass('normal'); } else if (quota < 1) { $usage.addClass('warning'); } - else { - $usage.addClass('above'); - if (!limitReachedDisplayed) { - limitReachedDisplayed = true; - common.alert(Messages._getKey('pinAboveLimitAlert', [prettyUsage, encodeURIComponent(window.location.hostname)]), null, true); - } - } + else { $usage.addClass('above'); } var $text = $('', {'class': 'usageText'}); $text.text(usage + ' / ' + prettyLimit); $limit.append($usage).append($text); @@ -1405,6 +1410,14 @@ define([ console.log('RPC handshake complete'); rpc = common.rpc = env.rpc = call; + common.getPinLimit(function (e, limit, plan, note) { + if (e) { return void console.error(e); } + common.account.limit = limit; + localStorage.plan = common.account.plan = plan; + common.account.note = note; + cb(); + }); + common.arePinsSynced(function (err, yes) { if (!yes) { common.resetPins(function (err) { @@ -1413,7 +1426,6 @@ define([ }); } }); - cb(); }); } else if (PINNING_ENABLED) { console.log('not logged in. pads will not be pinned'); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 4e5f2f056..787cedf07 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -170,6 +170,10 @@ define([ proxy[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); } + // copy User_hash into sessionStorage because cross-domain iframes + // on safari replaces localStorage with sessionStorage or something + if (sessionStorage) { sessionStorage.setItem('User_hash', localStorage.getItem('User_hash')); } + var localToken = tryParsing(localStorage.getItem(tokenKey)); if (localToken === null) { // if that number hasn't been set to localStorage, do so. diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 370b0e93d..d5d1143e9 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.MediaTag=t():e.MediaTag=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=82)}([function(e,t,n){"use strict";var r={IMAGE:"image",AUDIO:"audio",VIDEO:"video",PDF:"pdf",DASH:"dash",DOWNLOAD:"download",CRYPTO:"crypto",CLEAR_KEY:"clear-key",MEDIA_OBJECT:"media-object"};e.exports=r},function(e,t,n){"use strict";var r={MATCHER:"matcher",RENDERER:"renderer",FILTER:"filter",SANITIZER:"sanitizer"};e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=e.STACK_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin stack size exceed");if(e.snapshots[n].length>=e.SNAPSHOT_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin snapshots size exceed");var r=0;if(e.stacks[n].forEach(function(e){e.type===u.RENDERER&&r++}),r<1&&e.stacks[n].unshift(e.defaultPlugin),r>1)throw new Error("More of one renderer in the stack")}},{key:"return",value:function(t){e.start(t)}},{key:"run",value:function(t){var n=t.getId(),r=e.stacks[n].length,o=e.stacks[n][r-1];if(!o)throw console.log(e.stacks),new Error("Impossible to run a undefined plugin");o.process(t)}}]),e}();f.stacks={},f.STACK_LIMIT=1e3,f.snapshots={},f.SNAPSHOT_LIMIT=1e3,f.defaultPlugin=new s,e.exports=f},function(e,t,n){"use strict";var r={EVERY:"every",ANY:"any",ONCE:"once"};e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n1;){if(c){if("number"!=typeof e[t])throw new Error("E_UNSAFE_TYPE");if(e[t]>255)throw new Error("E_OUT_OF_BOUNDS")}if(255!==e[t])return void e[t]++;if(e[t]=0,0===t)throw new Error("E_NONCE_TOO_LARGE")}}},{key:"encodePrefix",value:function(e){return[65280,255].map(function(t,n){return(e&t)>>8*(1-n)})}},{key:"decodePrefix",value:function(e){return e[0]<<8|e[1]}},{key:"joinChunks",value:function(t){return new Uint8Array(t.reduce(function(t,n){return e.slice(t).concat(e.slice(n))},[]))}},{key:"slice",value:function(e){return Array.prototype.slice.call(e)}},{key:"getRandomKeyStr",value:function(){var t=e.Nacl,n=t.randomBytes(18);return t.util.encodeBase64(n)}},{key:"getKeyFromStr",value:function(t){return e.Nacl.util.decodeBase64(t)}},{key:"encrypt",value:function(t,n){var r=t,o=e.Nacl.randomBytes(24),i=e.Nacl.secretbox(r,o,n);if(i)return new Uint8Array(e.slice(o).concat(e.slice(i)));throw new Error}},{key:"decrypt",value:function(t,n,r){var o=e.Nacl,i=function(e){var n=new Event("decryptionProgress");n.percent=e/t.length*100,window.document.dispatchEvent(n)},u=e.createNonce(),a=0,c=t.subarray(0,2),f=e.decodePrefix(c),l={metadata:void 0},p=new Uint8Array(t.subarray(2,2+f)),y=o.secretbox.open(p,u,n);e.increment(u);try{l.metadata=JSON.parse(o.util.encodeUTF8(y))}catch(e){return r("E_METADATA_DECRYPTION")}if(!l.metadata)return r("NO_METADATA");var b=function(r){var c=a*s+2+f,l=c+s;a++;var p=new Uint8Array(t.subarray(c,l)),y=o.secretbox.open(p,u,n);if(e.increment(u),!y)return void r("DECRYPTION_FAILURE");i(Math.min(l,t.length)),r(void 0,y)},h=[];!function n(){b(function(o,i){return o?setTimeout(function(){r(o)}):i?a*s1?t[0]:window.location.protocol}},{key:"hostname",value:function(e){var t=e.getAttribute("src").split("://");return t.length>1?t[1].split("/")[0]:window.location.hostname}},{key:"source",value:function(e){return e.getAttribute("src")}},{key:"schemes",value:function(e){return/\w+:/.exec(e.getAttribute("src"))}},{key:"parse",value:function(t){return{protocol:e.protocol(t),hostname:e.hostname(t),src:e.source(t),type:e.type(t),extension:e.extension(t),mime:e.mime(t)}}}]),e}();e.exports=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=0&&f.mediaTypes.splice(t,1)},f.removeAllAllowedMediaTypes=function(e){e.forEach(function(e){f.removeAllowedMediaType(e)})},f.isAllowedMediaType=function(e){return f.mediaTypes.some(function(t){return t===e})},e.exports=f},,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n MediaTag cannot find a plugin able to renderer your content

","Download");r.processingEngine.setDefaultPlugin(b),r.CryptoFilter=f;var v=["image/png","image/jpeg","image/jpg","image/gif","audio/mp3","audio/ogg","audio/wav","audio/webm","video/mp4","video/ogg","video/webm","application/pdf","application/dash+xml","download"];r.CryptoFilter.setAllowedMediaTypes(v);var g=n(21),d=(n(13),n(0),new g);r.processingEngine.configure(d),e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=n(1),a=n(5),c=n(6),s=function(e){function t(e){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,u.SANITIZER,a.EVERY))}return i(t,e),t}(c);e.exports=s},,,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){var n=t.type.split("/"),r=t.type,o=n[0],i=n[1];s.isAllowedMediaType(r)?(e.setAttribute("data-type",t.type),e.type=o,e.extension=i,e.mime=r):console.log("Not allowed metadata, allowed ones are : ",s.getAllowedMediaTypes()),e.name=t.name,e.setAttribute("data-attr-type",t.type)}function i(e){var t=e.getAttribute("src"),n=e.getAttribute("data-crypto-key"),r=p.getKeyFromStr(n),i=new XMLHttpRequest;i.open("GET",t,!0),i.responseType="arraybuffer";var u=function(e){var t=new Event("decryptionError");t.message="string"==typeof e?e:e.message,window.document.dispatchEvent(t)};i.onload=function(){if(/^4/.test(""+this.status))return u("XHR_ERROR",""+this.status);var t=i.response;if(t){var n=new Uint8Array(t);p.decrypt(n,r,function(t,n){if(t)return u(t);var r=n.content,i=y.getBlobUrl(r,e.getMimeType()),a=new Event("decryption");a.blob=new Blob([r],{type:e.getMimeType()}),a.metadata=n.metadata,e.setAttribute("src",i),e.removeAttribute("data-crypto-key"),o(e,n.metadata),a.callback=function(){c.processingEngine.return(e)},window.document.dispatchEvent(a)})}},i.send(null)}var u=function(){function e(e,t){for(var n=0;n1;){if(f){if("number"!=typeof e[t])throw new Error("E_UNSAFE_TYPE");if(e[t]>255)throw new Error("E_OUT_OF_BOUNDS")}if(255!==e[t])return void e[t]++;if(e[t]=0,0===t)throw new Error("E_NONCE_TOO_LARGE")}}},{key:"encodePrefix",value:function(e){return[65280,255].map(function(t,n){return(e&t)>>8*(1-n)})}},{key:"decodePrefix",value:function(e){return e[0]<<8|e[1]}},{key:"joinChunks",value:function(t){return new Uint8Array(t.reduce(function(t,n){return e.slice(t).concat(e.slice(n))},[]))}},{key:"slice",value:function(e){return Array.prototype.slice.call(e)}},{key:"getRandomKeyStr",value:function(){var t=e.Nacl,n=t.randomBytes(18);return t.util.encodeBase64(n)}},{key:"getKeyFromStr",value:function(t){return e.Nacl.util.decodeBase64(t)}},{key:"encrypt",value:function(t,n){var r=t,o=e.Nacl.randomBytes(24),i=e.Nacl.secretbox(r,o,n);if(i)return new Uint8Array(e.slice(o).concat(e.slice(i)));throw new Error}},{key:"decrypt",value:function(t,n,r){var o=e.Nacl,i=function(e){var n=new Event("decryptionProgress");n.percent=e/t.length*100,window.document.dispatchEvent(n)},u=e.createNonce(),a=0,c=t.subarray(0,2),s=e.decodePrefix(c),f={metadata:void 0},p=new Uint8Array(t.subarray(2,2+s)),y=o.secretbox.open(p,u,n);e.increment(u);try{f.metadata=JSON.parse(o.util.encodeUTF8(y))}catch(e){return r("E_METADATA_DECRYPTION")}if(!f.metadata)return r("NO_METADATA");var h=function(r){var c=a*l+2+s,f=c+l;a++;var p=new Uint8Array(t.subarray(c,f)),y=o.secretbox.open(p,u,n);if(e.increment(u),!y)return void r("DECRYPTION_FAILURE");i(Math.min(f,t.length)),r(void 0,y)},b=[];!function n(){h(function(o,i){return o?setTimeout(function(){r(o)}):i?a*l1?t[0]:window.location.protocol}},{key:"hostname",value:function(e){var t=e.getAttribute("src").split("://");return t.length>1?t[1].split("/")[0]:window.location.hostname}},{key:"source",value:function(e){return e.getAttribute("src")}},{key:"schemes",value:function(e){return/\w+:/.exec(e.getAttribute("src"))}},{key:"parse",value:function(t){return{protocol:e.protocol(t),hostname:e.hostname(t),src:e.source(t),type:e.type(t),extension:e.extension(t),mime:e.mime(t)}}}]),e}();e.exports=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n=e.STACK_LIMIT)throw console.error(this.snapshots[n]),new Error("Plugin stack size exceed");if(this.snapshots[n].length>=e.SNAPSHOT_LIMIT)throw console.error(this.snapshots[n]),new Error("Plugin snapshots size exceed");var r=0;if(this.stacks[n].forEach(function(e){e.type===a.RENDERER&&r++}),r>1)throw console.error(this.snapshots[n]),new Error("More of one renderer in the stack");if(0===this.stacks[n].length&&!this.stats[n][a.RENDERER]){if(!this.defaultPlugin)throw new Error("No default plugin assignated");this.stacks[n].unshift(this.defaultPlugin)}}},{key:"return",value:function(e){var t=e.getId(),n=this.unstack(e);this.stats[t]||(this.stats[t]={}),this.stats[t][n.type]?this.stats[t][n.type]+=1:this.stats[t][n.type]=1,0===this.stacks[t].length&&n.type===a.RENDERER?this.run(e):n.type!==a.SANITIZER&&this.fill(e),this.snapshot(e),this.check(e),this.run(e)}},{key:"process",value:function(e){var t=e.getId(),n=this.stacks[t].length,r=this.stacks[t][n-1];if(!r)throw console.log(this.stacks),new Error("Impossible to run a undefined plugin");r.process(e)}},{key:"isStacked",value:function(e,t){var n=e.getId();return!(!this.stacks[n]||!this.stacks[n].includes(t))}},{key:"setDefaultPlugin",value:function(e){this.defaultPlugin=e}}]),e}();f.STACK_LIMIT=100,f.SNAPSHOT_LIMIT=100,e.exports=f},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n -1) { + var cpt = window.__CRYPTPAD_TEST__ = { + data: [], + getData: function () { + var data = JSON.stringify(cpt.data); + cpt.data = []; + return data; + } + }; + + // jshint -W103 + var errProto = (new Error()).__proto__; + var doLog = function (o) { + var s; + if (typeof(o) === 'object' && o.__proto__ === errProto) { + s = JSON.stringify([ o.message, o.stack ]); + } else if (typeof(s) !== 'string') { + try { + s = JSON.stringify(o); + } catch (e) { + s = String(o); + } + } + var out = [s]; + try { throw new Error(); } catch (e) { out.push(e.stack.split('\n')[3]); } + cpt.data.push({ type: 'log', val: out.join('') }); + }; + + window.console._error = window.console.error; + window.console._log = window.console.log; + window.console.error = function (e) { window.console._error(e); doLog(e); }; + window.console.log = function (l) { window.console._log(l); doLog(l); }; + + window.onerror = function (msg, url, lineNo, columnNo, e) { + cpt.data.push({ + type: 'report', + val: 'failed', + error: { + message: e ? e.message : msg, + stack: e ? e.stack : (url + ":" + lineNo) + } + }); + }; + require.onError = function (e) { + cpt.data.push({ + type: 'report', + val: 'failed', + error: { message: e.message, stack: e.stack } + }); + }; + out = function (f) { f(); }; + out.testing = true; + out.passed = function () { + cpt.data.push({ + type: 'report', + val: 'passed' + }); + }; + out.failed = function (reason) { + var e; + try { throw new Error(reason); } catch (err) { e = err; } + cpt.data.push({ + type: 'report', + val: 'failed', + error: { message: e.message, stack: e.stack } + }); + }; + } else { + out.testing = false; + } + return out; +}); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 6920c0b0d..bf08fcb07 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -500,8 +500,12 @@ define([ var todo = function (e, overLimit) { if (e) { return void console.error("Unable to get the pinned usage"); } if (overLimit) { + var message = Messages.pinLimitReachedAlert; + if (ApiConfig.noSubscriptionButton === true) { + message = Messages.pinLimitReachedAlertNoAccounts; + } $limit.show().click(function () { - Cryptpad.alert(Messages.pinLimitReachedAlert, null, true); + Cryptpad.alert(message, null, true); }); } }; diff --git a/www/common/toolbar2.js b/www/common/toolbar2.js index 57d113a48..8740606ee 100644 --- a/www/common/toolbar2.js +++ b/www/common/toolbar2.js @@ -10,7 +10,7 @@ define([ constants: {}, }; - var SPINNER_DISAPPEAR_TIME = 3000; + var SPINNER_DISAPPEAR_TIME = 1000; // Toolbar parts var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar'; @@ -33,6 +33,7 @@ define([ var LIMIT_CLS = Bar.constants.lag = 'cryptpad-limit'; var TITLE_CLS = Bar.constants.title = "cryptpad-title"; var NEWPAD_CLS = Bar.constants.newpad = "cryptpad-newpad"; + var UPGRADE_CLS = Bar.constants.upgrade = "cryptpad-upgrade"; // User admin menu var USERADMIN_CLS = Bar.constants.user = 'cryptpad-user-dropdown'; @@ -70,6 +71,7 @@ define([ var $userContainer = $('', { 'class': USER_CLS }).appendTo($topContainer); + $(' +
+

+ +
diff --git a/www/login/main.js b/www/login/main.js index 194c824ac..caa77fe4b 100644 --- a/www/login/main.js +++ b/www/login/main.js @@ -128,5 +128,16 @@ define([ }, 0); }, 100); }); + $('#register').on('click', function () { + if (sessionStorage) { + if ($uname.val()) { + sessionStorage.login_user = $uname.val(); + } + if ($passwd.val()) { + sessionStorage.login_pass = $passwd.val(); + } + } + window.location.href = '/register/'; + }); }); }); diff --git a/www/media/main.js b/www/media/main.js index 81820f8c0..bc861d699 100644 --- a/www/media/main.js +++ b/www/media/main.js @@ -6,6 +6,8 @@ define([ '/common/cryptpad-common.js', //'/common/visible.js', //'/common/notify.js', + 'pdfjs-dist/build/pdf', + 'pdfjs-dist/build/pdf.worker', '/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/file-saver/FileSaver.min.js', ], function ($, Crypto, realtimeInput, Toolbar, Cryptpad /*, Visible, Notify*/) { @@ -28,7 +30,7 @@ define([ var cryptKey = secret.keys && secret.keys.fileKeyStr; var fileId = secret.channel; var hexFileName = Cryptpad.base64ToHex(fileId); - var type = "image/png"; + // var type = "image/png"; var parsed = Cryptpad.parsePadUrl(window.location.href); var defaultName = Cryptpad.getDefaultName(parsed); @@ -57,7 +59,7 @@ define([ var $mt = $iframe.find('#encryptedFile'); $mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName); $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); - $mt.attr('data-type', type); + // $mt.attr('data-type', type); $(window.document).on('decryption', function (e) { var decrypted = e.originalEvent; @@ -98,6 +100,30 @@ define([ updateTitle(Cryptpad.initialName || getTitle() || defaultName); + /** + * Allowed mime types that have to be set for a rendering after a decryption. + * + * @type {Array} + */ + var allowedMediaTypes = [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'audio/mp3', + 'audio/ogg', + 'audio/wav', + 'audio/webm', + 'video/mp4', + 'video/ogg', + 'video/webm', + 'application/pdf', + 'application/dash+xml', + 'download' + ]; + + MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes); + MediaTag($mt[0]); Cryptpad.removeLoadingScreen(); diff --git a/www/pad/index.html b/www/pad/index.html index 4688001f6..3095c137f 100644 --- a/www/pad/index.html +++ b/www/pad/index.html @@ -4,6 +4,7 @@ CryptPad + div').addClass('half'); }); }); + + Test(function () { + $uname.val('test' + Math.random()); + $passwd.val('test'); + $confirm.val('test'); + $checkImport[0].checked = true; + $checkAcceptTerms[0].checked = true; + $register.click(); + + window.setTimeout(function () { + Cryptpad.findOKButton().click(); + }, 1000); + }); }); }); diff --git a/www/settings/index.html b/www/settings/index.html index f6ab459c9..28b36675e 100644 --- a/www/settings/index.html +++ b/www/settings/index.html @@ -40,6 +40,9 @@
Blog
+ + + @@ -105,7 +108,7 @@ - + diff --git a/www/settings/main.js b/www/settings/main.js index 546c93749..c7379db3a 100644 --- a/www/settings/main.js +++ b/www/settings/main.js @@ -3,7 +3,8 @@ define([ '/common/cryptpad-common.js', '/common/cryptget.js', '/common/mergeDrive.js', - '/bower_components/file-saver/FileSaver.min.js' + '/bower_components/file-saver/FileSaver.min.js', + '/customize/header.js', ], function ($, Cryptpad, Crypt, Merge) { var saveAs = window.saveAs; @@ -328,19 +329,6 @@ define([ $(function () { var $main = $('#mainBlock'); - // Language selector - var $sel = $('#language-selector'); - Cryptpad.createLanguageSelector(undefined, $sel); - $sel.find('button').addClass('btn').addClass('btn-secondary'); - $sel.show(); - - // User admin menu - var $userMenu = $('#user-menu'); - var userMenuCfg = { - $initBlock: $userMenu - }; - var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg); - $userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $(window).click(function () { $('.cryptpad-dropdown').hide(); diff --git a/www/slide/index.html b/www/slide/index.html index faf9bc1c8..2f5e675a9 100644 --- a/www/slide/index.html +++ b/www/slide/index.html @@ -4,6 +4,7 @@ CryptPad + Blog
+ + + @@ -105,7 +108,7 @@ - + diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js index 8a8f6e696..dce656473 100644 --- a/www/whiteboard/main.js +++ b/www/whiteboard/main.js @@ -296,7 +296,7 @@ window.canvas = canvas; Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg); var configTb = { - displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit'], + displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'], userList: UserList.getToolbarConfig(), share: { secret: secret,