From 812d8f87708a3075ccf0c1e5aea7ac902afa85b2 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 27 Mar 2020 15:47:59 +0100 Subject: [PATCH 01/21] Deprecate 'skip PCS' feature --- www/common/common-ui-elements.js | 45 +----- www/common/sframe-app-framework.js | 6 - www/common/sframe-common.js | 5 +- www/common/toolbar3.js | 2 +- www/poll/inner.js | 7 - www/settings/inner.js | 246 ----------------------------- 6 files changed, 4 insertions(+), 307 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 60a3bcfc5..22836a234 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2796,18 +2796,6 @@ define([ var $advanced; - var $advancedContainer = $('
'); - var priv = common.getMetadataMgr().getPrivateData(); - var c = (priv.settings.general && priv.settings.general.creation) || {}; - if (AppConfig.displayCreationScreen && common.isLoggedIn() && c.skip) { - var $cboxLabel = $(UI.createCheckbox('cp-app-toolbar-creation-advanced', - Messages.creation_newPadModalAdvanced, true)) - .appendTo($advancedContainer); - $advanced = $cboxLabel.find('input'); - $description.append('
'); - $description.append(Messages.creation_newPadModalDescriptionAdvanced); - } - var $container = $('
'); var i = 0; var types = AppConfig.availablePadTypes.filter(function (p) { @@ -2872,7 +2860,7 @@ define([ }); - $modal.find('.cp-modal').append($container).append($advancedContainer); + $modal.find('.cp-modal').append($container); window.setTimeout(function () { $modal.show(); $modal.focus(); @@ -3117,15 +3105,6 @@ define([ right ]); - var settings = h('div.cp-creation-remember', [ - UI.createCheckbox('cp-creation-remember', Messages.dontShowAgain, false), - createHelper('/settings/#creation', Messages.creation_settings), - h('div.cp-creation-remember-help.cp-creation-slider', [ - h('span.fa.fa-exclamation-circle.cp-creation-warning'), - Messages.creation_rememberHelp - ]) - ]); - var createDiv = h('div.cp-creation-create'); var $create = $(createDiv); @@ -3134,7 +3113,6 @@ define([ owned, expire, password, - settings, templates, createDiv ])).appendTo($creation); @@ -3282,16 +3260,6 @@ define([ $creation.focus(); }); - // Display settings help when checkbox checked - $creation.find('#cp-creation-remember').on('change', function () { - if ($(this).is(':checked')) { - $creation.find('.cp-creation-remember-help:not(.active)').addClass('active'); - return; - } - $creation.find('.cp-creation-remember-help').removeClass('active'); - $creation.focus(); - }); - // Keyboard shortcuts $creation.find('#cp-creation-expire-val').keydown(function (e) { if (e.which === 9) { @@ -3309,9 +3277,6 @@ define([ if (!cfg.owned && typeof cfg.owned !== "undefined") { $creation.find('#cp-creation-owned').prop('checked', false); } - if (cfg.skip) { - $creation.find('#cp-creation-remember').prop('checked', true).trigger('change'); - } UIElements.setExpirationValue(cfg.expire, $creation); // Create the pad @@ -3354,14 +3319,6 @@ define([ var create = function () { var val = getFormValues(); - var skip = $('#cp-creation-remember').is(':checked'); - common.setAttribute(['general', 'creation', 'skip'], skip, function (e) { - if (e) { return void console.error(e); } - }); - common.setAttribute(['general', 'creation', 'noTemplate'], skip, function (e) { - if (e) { return void console.error(e); } - }); - common.setAttribute(['general', 'creation', 'owned'], val.owned, function (e) { if (e) { return void console.error(e); } }); diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index a90877ff1..e1378bbac 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -402,12 +402,6 @@ define([ Thumb.initPadThumbnails(common, options.thumbnail); } } - - var skipTemp = Util.find(privateDat, ['settings', 'general', 'creation', 'noTemplate']); - var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']); - if (newPad && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) { - common.openTemplatePicker(); - } }); }; var onConnectionChange = function (info) { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 8d04c6263..be85f2fdc 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -293,10 +293,9 @@ define([ var priv = ctx.metadataMgr.getPrivateData(); if (priv.isNewFile) { var c = (priv.settings.general && priv.settings.general.creation) || {}; - var skip = !AppConfig.displayCreationScreen || (c.skip && !priv.forceCreationScreen); // If this is a new file but we have a hash in the URL and pad creation screen is // not displayed, then display an error... - if (priv.isDeleted && (!funcs.isLoggedIn() || skip)) { + if (priv.isDeleted && !funcs.isLoggedIn()) { UI.errorLoadingScreen(Messages.inactiveError, false, function () { UI.addLoadingScreen(); return void funcs.createPad({}, waitFor()); @@ -305,7 +304,7 @@ define([ } // Otherwise, if we don't display the screen, it means it is not a deleted pad // so we can continue and start realtime... - if (!funcs.isLoggedIn() || skip) { + if (!funcs.isLoggedIn()) { return void funcs.createPad(c, waitFor()); } // If we display the pad creation screen, it will handle deleted pads directly diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 13f7b9f50..db1d0288e 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -941,7 +941,7 @@ MessengerUI, Messages) { id: 'cp-app-toolbar-creation-advanced', href: origin }, - content: ' ' + Messages.creation_appMenuName + content: ' ' + Messages.creation_appMenuName // XXX change value }); var dropdownConfig = { text: '', // Button initial text diff --git a/www/poll/inner.js b/www/poll/inner.js index 38ed1b79c..5c9f9f182 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -1066,13 +1066,6 @@ define([ common.openPadChat(function () {}); UI.removeLoadingScreen(); - var privateDat = metadataMgr.getPrivateData(); - var skipTemp = Util.find(privateDat, - ['settings', 'general', 'creation', 'noTemplate']); - var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']); - if (isNew && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) { - common.openTemplatePicker(); - } }; var onError = function (info) { diff --git a/www/settings/inner.js b/www/settings/inner.js index 7dbe8b3ce..5541277ed 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -62,12 +62,6 @@ define([ 'cp-settings-safe-links', 'cp-settings-userfeedback', ], - 'creation': [ - 'cp-settings-creation-owned', - 'cp-settings-creation-expire', - 'cp-settings-creation-skip', - 'cp-settings-creation-template' - ], 'drive': [ 'cp-settings-drive-duplicate', 'cp-settings-thumbnails', @@ -101,9 +95,6 @@ define([ } }; - if (!AppConfig.displayCreationScreen) { - delete categories.creation; - } if (AppConfig.disableFeedback) { var feedbackIdx = categories.account.indexOf('cp-settings-userfeedback'); categories.account.splice(feedbackIdx, 1); @@ -610,242 +601,6 @@ define([ cb($cbox); }, true); - // Pad Creation settings - - var setHTML = function (e, html) { - e.innerHTML = html; - return e; - }; - create['creation-owned'] = function () { - if (!common.isLoggedIn()) { return; } - var owned = h('div.cp-settings-creation-owned.cp-sidebarlayout-element', [ - h('label', [ - Messages.creation_ownedTitle - ]), - setHTML(h('p.cp-sidebarlayout-description'), - Messages.creation_owned1 + '
' + Messages.creation_owned2), - h('input#cp-creation-owned-true.cp-creation-owned-value', { - type: 'radio', - name: 'cp-creation-owned', - value: 1, - checked: 'checked' - }), - h('label', { 'for': 'cp-creation-owned-true' }, Messages.creation_ownedTrue), - h('input#cp-creation-owned-false.cp-creation-owned-value', { - type: 'radio', - name: 'cp-creation-owned', - value: 0, - }), - h('label', { 'for': 'cp-creation-owned-false' }, Messages.creation_ownedFalse), - h('span.fa.fa-check', {title: Messages.saved}), - h('span.fa.fa-spinner.fa-pulse'), - ]); - - var $owned = $(owned); - - var $ok = $owned.find('.fa-check').hide(); - var $spinner = $owned.find('.fa-spinner').hide(); - - $owned.find('input').change(function () { - $spinner.show(); - $ok.hide(); - var val = parseInt($owned.find('[name="cp-creation-owned"]:checked').val()); - common.setAttribute(['general', 'creation', 'owned'], val, function (e) { - if (e) { return void console.error(e); } - $spinner.hide(); - $ok.show(); - }); - }); - common.getAttribute(['general', 'creation', 'owned'], function (e, val) { - if (!val && typeof val !== "undefined") { - $owned.find('#cp-creation-owned-false').attr('checked', true); - } - }); - - return $owned; - }; - create['creation-expire'] = function () { - if (!common.isLoggedIn()) { return; } - var expire = h('div.cp-settings-creation-expire.cp-sidebarlayout-element', [ - h('label', [ - Messages.creation_expireTitle - ]), - setHTML(h('p.cp-sidebarlayout-description'), - Messages.creation_expire1 + '
' + Messages.creation_expire2), - h('input#cp-creation-expire-false.cp-creation-expire-value', { - type: 'radio', - name: 'cp-creation-expire', - value: 0, - checked: 'checked' - }), - h('label', { 'for': 'cp-creation-expire-false' }, Messages.creation_expireFalse), - h('input#cp-creation-expire-true.cp-creation-expire-value', { - type: 'radio', - name: 'cp-creation-expire', - value: 1 - }), - h('label', { 'for': 'cp-creation-expire-true' }, [ - Messages.creation_expireTrue, - h('span.cp-creation-expire-picker', [ - h('input#cp-creation-expire-val', { - type: "number", - min: 1, - max: 100, - value: 3 - }), - h('select#cp-creation-expire-unit', [ - h('option', { value: 'hour' }, Messages.creation_expireHours), - h('option', { value: 'day' }, Messages.creation_expireDays), - h('option', { - value: 'month', - selected: 'selected' - }, Messages.creation_expireMonths) - ]) - ]) - ]), - h('span.fa.fa-check', {title: Messages.saved}), - h('span.fa.fa-spinner.fa-pulse'), - ]); - - var $expire = $(expire); - - var $ok = $expire.find('.fa-check').hide(); - var $spinner = $expire.find('.fa-spinner').hide(); - - var getValue = function () { - if(!parseInt($expire.find('[name="cp-creation-expire"]:checked').val())) { return 0; } - var unit = 0; - switch ($expire.find('#cp-creation-expire-unit').val()) { - case "hour" : unit = 3600; break; - case "day" : unit = 3600 * 24; break; - case "month": unit = 3600 * 24 * 30; break; - default: unit = 0; - } - return ($expire.find('#cp-creation-expire-val').val() || 0) * unit; - }; - $expire.find('input, select').change(function () { - $spinner.show(); - $ok.hide(); - common.setAttribute(['general', 'creation', 'expire'], getValue(), function (e) { - if (e) { return void console.error(e); } - $spinner.hide(); - $ok.show(); - }); - }); - common.getAttribute(['general', 'creation', 'expire'], function (e, val) { - UIElements.setExpirationValue(val, $expire); - }); - - return $expire; - }; - create['creation-skip'] = function () { - if (!common.isLoggedIn()) { return; } - var skip = h('div.cp-settings-creation-skip.cp-sidebarlayout-element', [ - h('label', [ - Messages.settings_creationSkip - ]), - setHTML(h('p.cp-sidebarlayout-description'), Messages.settings_creationSkipHint), - h('input#cp-creation-skip-true.cp-creation-skip-value', { - type: 'radio', - name: 'cp-creation-skip', - value: 1, - }), - h('label', { 'for': 'cp-creation-skip-true' }, Messages.settings_creationSkipTrue), - h('input#cp-creation-skip-false.cp-creation-skip-value', { - type: 'radio', - name: 'cp-creation-skip', - value: 0, - checked: 'checked' - }), - h('label', { 'for': 'cp-creation-skip-false' }, Messages.settings_creationSkipFalse), - h('span.fa.fa-check', {title: Messages.saved}), - h('span.fa.fa-spinner.fa-pulse'), - ]); - - var $div = $(skip); - - var $ok = $div.find('.fa-check').hide(); - var $spinner = $div.find('.fa-spinner').hide(); - - $div.find('input').change(function () { - $spinner.show(); - $ok.hide(); - var val = parseInt($div.find('[name="cp-creation-skip"]:checked').val()); - // If we don't skip the pad creation screen, we dont' need settings to hide the templates - // modal - if (!val) { - $('.cp-settings-creation-template').addClass('cp-settings-creation-skipped'); - } else { - $('.cp-settings-creation-template').removeClass('cp-settings-creation-skipped'); - } - common.setAttribute(['general', 'creation', 'skip'], val, function (e) { - if (e) { return void console.error(e); } - $spinner.hide(); - $ok.show(); - }); - }); - common.getAttribute(['general', 'creation', 'skip'], function (e, val) { - if (val) { - $div.find('#cp-creation-skip-true').attr('checked', true); - return; - } - // If we don't skip the pad creation screen, we dont' need settings to hide the templates - // modal - $('.cp-settings-creation-template').addClass('cp-settings-creation-skipped'); - }); - - return $div; - }; - create['creation-template'] = function () { - var skip = h('div.cp-settings-creation-template.cp-sidebarlayout-element', [ - h('label', [ - Messages.settings_templateSkip - ]), - setHTML(h('p.cp-sidebarlayout-description'), Messages.settings_templateSkipHint), - h('input#cp-creation-template-true.cp-creation-template-value', { - type: 'radio', - name: 'cp-creation-template', - value: 1, - }), - h('label', { 'for': 'cp-creation-template-true' }, Messages.settings_creationSkipTrue), - h('input#cp-creation-template-false.cp-creation-template-value', { - type: 'radio', - name: 'cp-creation-template', - value: 0, - checked: 'checked' - }), - h('label', { 'for': 'cp-creation-template-false' }, Messages.settings_creationSkipFalse), - h('span.fa.fa-check', {title: Messages.saved}), - h('span.fa.fa-spinner.fa-pulse'), - ]); - - var $div = $(skip); - - var $ok = $div.find('.fa-check').hide(); - var $spinner = $div.find('.fa-spinner').hide(); - - $div.find('input').change(function () { - $spinner.show(); - $ok.hide(); - var val = parseInt($div.find('[name="cp-creation-template"]:checked').val()); - common.setAttribute(['general', 'creation', 'noTemplate'], val, function (e) { - if (e) { return void console.error(e); } - $spinner.hide(); - $ok.show(); - }); - }); - common.getAttribute(['general', 'creation', 'noTemplate'], function (e, val) { - if (val) { - $div.find('#cp-creation-template-true').attr('checked', true); - } - }); - - return $div; - }; - - - - // Drive settings create['drive-duplicate'] = function () { @@ -1720,7 +1475,6 @@ define([ if (key === 'cursor') { $category.append($('', {'class': 'fa fa-i-cursor' })); } if (key === 'code') { $category.append($('', {'class': 'fa fa-file-code-o' })); } if (key === 'pad') { $category.append($('', {'class': 'fa fa-file-word-o' })); } - if (key === 'creation') { $category.append($('', {'class': 'fa fa-plus-circle' })); } if (key === 'security') { $category.append($('', {'class': 'fa fa-lock' })); } if (key === 'subscription') { $category.append($('', {'class': 'fa fa-star-o' })); } From 8370f3121c07416e78660d590374ed51f9e5d1c3 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 6 Apr 2020 16:26:29 +0200 Subject: [PATCH 02/21] Remove XXX --- www/common/toolbar3.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index db1d0288e..13f7b9f50 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -941,7 +941,7 @@ MessengerUI, Messages) { id: 'cp-app-toolbar-creation-advanced', href: origin }, - content: ' ' + Messages.creation_appMenuName // XXX change value + content: ' ' + Messages.creation_appMenuName }); var dropdownConfig = { text: '', // Button initial text From 6ea958c7bdc34bf63ee6fa4f64123785a3837067 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 6 Apr 2020 16:27:15 +0200 Subject: [PATCH 03/21] Translated using Weblate (Finnish) Currently translated at 72.2% (899 of 1246 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fi/ --- www/common/translations/messages.fi.json | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/www/common/translations/messages.fi.json b/www/common/translations/messages.fi.json index 2743d44a8..8a4e36139 100644 --- a/www/common/translations/messages.fi.json +++ b/www/common/translations/messages.fi.json @@ -281,7 +281,7 @@ "profile_description": "Kuvaus", "profile_fieldSaved": "Uusi arvo tallennettu: {0}", "profile_viewMyProfile": "Näytä oma profiili", - "userlist_addAsFriendTitle": "Lähetä ystäväpyyntö käyttäjälle \"{0}\"", + "userlist_addAsFriendTitle": "Lähetä kontaktipyyntö käyttäjälle \"{0}\"", "contacts_title": "Yhteystiedot", "contacts_addError": "Virhe lisätessä yhteystietoa listaan", "contacts_added": "Yhteystietopyyntö hyväksytty.", @@ -301,7 +301,7 @@ "contacts_confirmRemoveHistory": "Oletko varma, että haluat tyhjentää keskusteluhistorian pysyvästi? Tietoja ei voida palauttaa", "contacts_removeHistoryServerError": "Keskusteluhistorian poistamisessa tapahtui virhe. Yritä myöhemmin uudelleen", "contacts_fetchHistory": "Hae vanhempia viestejä", - "contacts_friends": "Kaverit", + "contacts_friends": "Kontaktit", "contacts_rooms": "Keskusteluhuoneet", "contacts_leaveRoom": "Lähde keskusteluhuoneesta", "contacts_online": "Toinen käyttäjä tästä keskusteluhuoneesta on online-tilassa", @@ -354,7 +354,7 @@ "fm_info_root": "Voit luoda haluamasi määrän sisäkkäisiä kansioita tiedostojesi järjestämiseen.", "fm_info_unsorted": "Sisältää kaikki käyttämäsi tiedostot, joita ei ole vielä lajiteltu \"Tiedostot\"-näkymässä tai siirretty roskakoriin.", "fm_info_template": "Sisältää kaikki mallipohjiksi tallennetut padit, joita voit käyttää uudelleen luodessasi uuden padin.", - "fm_info_recent": "Näytä viimeksi muokatut tai avatut padit.", + "fm_info_recent": "Tässä näytetään sinun tai yhteistyökumppaniesi äskettäin avaamat tai muokkaamat padit.", "fm_info_trash": "Tyhjennä roskakorisi vapauttaaksesi CryptDrive-tallennustilaa.", "fm_info_allFiles": "Sisältää kaikki tiedostot \"Dokumentit\"- \"Lajittelemattomat\"- ja \"Roskakori\"-näkymistä. Et voi siirtää tai poistaa tiedostoja täältä.", "fm_info_anonymous": "Et ole kirjautunut sisään. Padisi vanhenevat kolmen kuukauden kuluttua ((lue lisää)). Padit säilytetään paikallisesti selaimessasi, joten selaimen historiatietojen tyhjentäminen saattaa hävittää ne.
Rekisteröidy tai kirjaudu sisään säilyttääksesi padisi palvelimella.
", @@ -513,7 +513,7 @@ "settings_pinningError": "Jokin meni pieleen", "settings_usageAmount": "Kiinnitetyt padisi käyttävät {0}Mt", "settings_logoutEverywhereButton": "Kirjaudu ulos", - "settings_logoutEverywhereTitle": "Kirjaudu ulos kaikkialta", + "settings_logoutEverywhereTitle": "Sulje etäistunnot", "settings_logoutEverywhere": "Pakota uloskirjautuminen kaikista web-sessioista", "settings_logoutEverywhereConfirm": "Oletko varma? Joudut kirjautumaan kaikilla laitteillasi uudelleen sisään.", "settings_driveDuplicateTitle": "Omistettujen padien kaksoiskappaleet", @@ -534,8 +534,8 @@ "settings_creationSkipFalse": "Näytä", "settings_templateSkip": "Ohita mallipohjan valinta-dialogi", "settings_templateSkipHint": "Luodessasi uutta padia sinulta kysytään, haluatko käyttää mallipohjaa, jos sinulla on tallennettuja mallipohjia tälle padityypille. Tällä asetuksella voit valita, ettei mallipohjan valinta-dialogia näytetä ja siten mallipohjia ei koskaan käytetä.", - "settings_ownDriveTitle": "Ota käyttöön viimeisimmät tiliominaisuudet", - "settings_ownDriveHint": "Teknisistä syistä vanhemmilla käyttäjätileillä ei suoraan ole pääsyä CryptPadin uusimpiin ominaisuuksiin. Ilmainen päivitys uuteen käyttäjätiliin valmistelee CryptDrivesi tulevia ominaisuuksia varten häiritsemättä tavanomaista toimintaasi.", + "settings_ownDriveTitle": "Päivitä käyttäjätili", + "settings_ownDriveHint": "Vanhemmilla käyttäjätileillä ei ole teknisistä syistä pääsyä CryptPadin uusimpiin ominaisuuksiin. Ilmainen päivitys uuteen käyttäjätiliin valmistelee CryptDrivesi tulevia ominaisuuksia varten häiritsemättä tavanomaista toimintaasi.", "settings_ownDriveButton": "Päivitä käyttäjätilisi", "settings_ownDriveConfirm": "Käyttäjätilin päivitykseen voi mennä jonkin aikaa. Joudut kirjautumaan uudelleen sisään kaikilla laitteillasi. Oletko varma, että haluat aloittaa päivityksen?", "settings_ownDrivePending": "Käyttäjätiliäsi päivitetään. Ole hyvä, äläkä sulje tai lataa tätä sivua uudelleen, ennen kuin toimenpide on valmis.", @@ -850,7 +850,7 @@ "features_f_social": "Sosiaaliset sovellukset", "features_f_social_note": "Luo käyttäjäprofiili, käytä avatar-kuvaa, keskustele yhteystietojen kanssa", "features_f_file1": "Lataa ja jaa tiedostoja", - "features_f_file1_note": "Jaa tiedostoja kavereidesi kanssa tai upota ne padeihisi", + "features_f_file1_note": "Jaa tiedostoja kontaktiesi kanssa tai upota ne padeihisi", "features_f_storage1": "Pysyvä tallennustila (50Mt)", "features_f_storage1_note": "CryptDriveen tallennettuja padeja ei koskaan poisteta käyttämättömyyden takia", "features_f_register": "Rekisteröidy ilmaiseksi", @@ -953,7 +953,7 @@ "a": "Rekisteröityneille käyttäjille on tarjolla joitakin toimintoja, jotka eivät ole saatavilla rekisteröitymättömille käyttäjille. Löydät nämä toiminnot luomastamme kaaviosta." }, "share": { - "q": "Miten jaan salattuja padeja kavereideni kanssa?", + "q": "Miten jaan salattuja padeja kontaktieni kanssa?", "a": "CryptPad laittaa URL-osoitteessa padisi salaisen salausavaimen #-merkin jälkeen. Tämän merkin jälkeen laitettuja tietoja ei lähetetä palvelimelle, joten emme pääse koskaan käyttämään salausavaimiasi. Jakaessasi linkin padiin jaat oikeuden lukea ja käyttää sitä." }, "remove": { @@ -985,7 +985,7 @@ "title": "Muita kysymyksiä", "pay": { "q": "Miksi minun täytyisi maksaa, kun niin monet toiminnot ovat ilmaisia?", - "a": "Annamme tukijoillemme lisätallennustilaa ja mahdollisuuden kasvattaa kavereiden tallennustilakiintiöitä (lue lisää).

Näiden lyhytaikaisten etujen lisäksi premium-tilaus auttaa rahoittamaan CryptPadin jatkuvaa, aktiivista kehitystyötä. Tähän kuuluu bugien korjaamista, uusien ominaisuuksien lisäämistä ja CryptPad-instanssien pystyttämisen ja ylläpidon helpottamista. Lisäksi autat näyttämään muille palveluntarjoajille, että ihmiset ovat valmiita tukemaan yksityisyyttä parantavia teknologioita. Toivomme, että käyttäjätietojen myymiseen perustuvat liiketoimintamallit jäävät lopulta menneeseen.

Lopuksi, tarjoamme suurimman osan CryptPadin toiminnallisuudesta ilmaiseksi, koska uskomme yksityisyyden kuuluvan kaikille - ei vain niille, joilla on varaa maksaa siitä. Tukemalla meitä autat tarjoamaan heikommassa asemassa oleville väestöille pääsyn näihin peruspalveluihin." + "a": "Annamme tukijoillemme lisätallennustilaa ja mahdollisuuden kasvattaa kontaktien tallennustilakiintiöitä (lue lisää).

Näiden lyhytaikaisten etujen lisäksi premium-tilaus auttaa rahoittamaan CryptPadin jatkuvaa, aktiivista kehitystyötä. Tähän kuuluu bugien korjaamista, uusien ominaisuuksien lisäämistä ja CryptPad-instanssien pystyttämisen ja ylläpidon helpottamista. Lisäksi autat näyttämään muille palveluntarjoajille, että ihmiset ovat valmiita tukemaan yksityisyyttä parantavia teknologioita. Toivomme, että käyttäjätietojen myymiseen perustuvat liiketoimintamallit jäävät lopulta menneeseen.

Lopuksi, tarjoamme suurimman osan CryptPadin toiminnallisuudesta ilmaiseksi, koska uskomme yksityisyyden kuuluvan kaikille - ei vain niille, joilla on varaa maksaa siitä. Tukemalla meitä autat tarjoamaan heikommassa asemassa oleville väestöille pääsyn näihin peruspalveluihin." }, "goal": { "q": "Mitkä ovat tavoitteenne?", @@ -1047,7 +1047,7 @@ "colors": "Vaihda tekstin ja taustan väriä ja -painikkeilla" }, "poll": { - "decisions": "Tee päätöksiä luotettujen ystävien kesken", + "decisions": "Tee päätöksiä luotettujen kontaktien kesken", "options": "Ehdota vaihtoehtoja ja tuo ilmi mielipiteesi", "choices": "Napsauta sarakkeesi soluja valitaksesi kyllä- (), ehkä- (~), tai ei () -vaihtoehdon", "submit": "Napsauta Lähetä tehdäksesi valintasi näkyviksi muille" @@ -1058,12 +1058,12 @@ "embed": "Upota kuvia kovalevyltäsi tai CryptDrivestasi ja vie ne PNG-tiedostomuodossa kovalevyllesi tai CryptDriveesi " }, "kanban": { - "add": "Lisää uusia tauluja oikeassa yläkulmassa olevalla -painikkeella", - "task": "Siirrä kohtia raahaamalla ja pudottamalla ne yhdestä taulusta toiseen", - "color": "Vaihda värejä napsauttamalla taulun otsikon vieressä olevaa värillistä osaa" + "add": "Lisää uusia kortteja ja tauluja -painikkeella", + "task": "Siirrä kohtia raahaamalla ja pudottamalla, raahaa roskakoriin poistaaksesi", + "color": "Muokkaa otsikoita, sisältöä, tunnisteita ja värejä korttien ja taulujen otsikoiden vieressä olevalla span class=\"fa fa-pencil\">
-painikkeella" } }, "driveReadmeTitle": "Mikä on CryptPad?", "readme_welcome": "Tervetuloa CryptPadiin!", - "readme_p1": "Tervetuloa CryptPadiin, täällä voit tehdä muistiinpanoja yksin tai ystäviesi kanssa." + "readme_p1": "Tervetuloa CryptPadiin, täällä voit tehdä muistiinpanoja yksin tai kontaktiesi kanssa." } From 3f126f1bec00fbd40c8d3ecfef30e1aec8ba4d93 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 6 Apr 2020 16:27:15 +0200 Subject: [PATCH 04/21] Translated using Weblate (German) Currently translated at 100.0% (1246 of 1246 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index 415e38448..86e2300e4 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -938,7 +938,7 @@ "creation_expiration": "Ablaufdatum", "creation_passwordValue": "Passwort", "creation_propertiesTitle": "Verfügbarkeit", - "creation_appMenuName": "Fortgeschrittener Modus (Strg + E)", + "creation_appMenuName": "Neues Pad (Strg + E)", "creation_newPadModalDescription": "Klicke auf einen Pad-Typ, um das entsprechende Pad zu erstellen. Du kannst auch die Tab-Taste für die Auswahl und die Enter-Taste zum Bestätigen benutzen.", "creation_newPadModalDescriptionAdvanced": "Du kannst das Kästchen markieren (oder den Wert mit der Leertaste ändern), um den Dialog bei der Pad-Erstellung anzuzeigen (für eigene oder auslaufende Dokumente etc.).", "creation_newPadModalAdvanced": "Dialog bei der Pad-Erstellung anzeigen", From e1be2414064061d15f01acd1d3a262d63132f621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Mon, 6 Apr 2020 15:42:14 +0100 Subject: [PATCH 05/21] filepicker thumbnail style --- www/filepicker/app-filepicker.less | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/www/filepicker/app-filepicker.less b/www/filepicker/app-filepicker.less index d31b4ddb1..c0b9e7789 100644 --- a/www/filepicker/app-filepicker.less +++ b/www/filepicker/app-filepicker.less @@ -27,8 +27,6 @@ } .cp-filepicker-content-element { - @darker: darken(@colortheme_modal-fg, 30%); - width: 125px; //min-width: 200px; //height: 1em; @@ -45,8 +43,9 @@ cursor: pointer; background-color: @colortheme_modal-bg; - box-shadow: 2px 2px 5px #000; - color: @darker; + + border: 1px solid @colortheme_logo-2; + color: @colortheme_logo-2; transition: all 0.1s; From f95c385c82ca04daa9d0546ee6d68b79d48006ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Mon, 6 Apr 2020 15:42:26 +0100 Subject: [PATCH 06/21] filepicker button --- customize.dist/src/less2/include/modal.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/customize.dist/src/less2/include/modal.less b/customize.dist/src/less2/include/modal.less index 635a65b71..12d3c908c 100644 --- a/customize.dist/src/less2/include/modal.less +++ b/customize.dist/src/less2/include/modal.less @@ -1,5 +1,8 @@ @import (reference) "./colortheme-all.less"; @import (reference) "./variables.less"; +@import (reference) './buttons.less'; + + .modal_base() { font-family: @colortheme_font; @@ -36,6 +39,8 @@ background-color: @colortheme_modal-dim; .cp-modal { + .buttons_main(); + background-color: @colortheme_modal-bg; color: @colortheme_modal-fg; box-shadow: @variables_shadow; @@ -75,6 +80,7 @@ background-color: @colortheme_modal-input-fg; color: @cryptpad_text_col; border: 1px solid @colortheme_modal-input; + width: auto; } } From a834d653aad8e6d66d6e9226d2913a88ad5f85e0 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 6 Apr 2020 16:48:42 +0200 Subject: [PATCH 07/21] Fix download button size in mediatag preview --- customize.dist/src/less2/include/modals-ui-elements.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less index 62b175f0e..0cc580ec0 100644 --- a/customize.dist/src/less2/include/modals-ui-elements.less +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -125,6 +125,9 @@ video, iframe { margin-bottom: -5px; } + button { + line-height: 1.5; + } & > iframe { width: 100%; height: 100%; From 9ef755c07b7d0c7af8bef337554b877cd06740d7 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 6 Apr 2020 11:26:34 -0400 Subject: [PATCH 08/21] update minor version and codename --- customize.dist/pages.js | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index d782e4a16..63cbbf154 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -107,7 +107,7 @@ define([ ])*/ ]) ]), - h('div.cp-version-footer', "CryptPad v3.14.0 (OrienteCaveRat)") + h('div.cp-version-footer', "CryptPad v3.15.0 (PigFootedBandicoot)") ]); }; diff --git a/package-lock.json b/package-lock.json index 8d0cebc6b..cefaca30d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "3.14.0", + "version": "3.15.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 668eb53a0..cd684660f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "3.14.0", + "version": "3.15.0", "license": "AGPL-3.0+", "repository": { "type": "git", From a57bf94058f983a15f451b175da6df021eeeb80b Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 6 Apr 2020 11:31:31 -0400 Subject: [PATCH 09/21] remove XXX note --- lib/workers/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 7d8c1dcc7..3190741d3 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -135,8 +135,6 @@ Workers.initializeIndexWorkers = function (Env, config, _cb) { var state = workers[workerIndex]; - // XXX insert a queue here to prevent timeouts - const txid = guid(); msg.txid = txid; msg.pid = PID; From 2e290a66675a8a18096aad0dda53839044095b69 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 6 Apr 2020 18:21:07 -0400 Subject: [PATCH 10/21] last minute fixes and notes --- www/auth/main.js | 4 +++- www/common/inner/common-mediatag.js | 3 ++- www/common/outer/roster.js | 4 +++- www/common/sframe-common-file.js | 4 +++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/www/auth/main.js b/www/auth/main.js index 84a17d922..c155204ec 100644 --- a/www/auth/main.js +++ b/www/auth/main.js @@ -71,7 +71,7 @@ define([ // Get contacts and extract their avatar channel and key var getData = function (obj, href) { var parsed = Hash.parsePadUrl(href); - if (parsed.type !== "file") { return; } + if (!parsed || parsed.type !== "file") { return; } // XXX var secret = Hash.getSecrets('file', parsed.hash); if (!secret.keys || !secret.channel) { return; } obj.avatarKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey); @@ -81,6 +81,7 @@ define([ contacts.friends = proxy.friends || {}; Object.keys(contacts.friends).map(function (key) { var friend = contacts.friends[key]; + // if (!friend) { return; } // XXX how should this be handled? var ret = { edPublic: friend.edPublic, name: friend.displayName, @@ -90,6 +91,7 @@ define([ }); Object.keys(contacts.teams).map(function (key) { var team = contacts.teams[key]; + // if (!team) { return; } // XXX how should this be handled. Is this possible? var avatar = team.metadata && team.metadata.avatar; var ret = { edPublic: team.keys && team.keys.drive && team.keys.drive.edPublic, diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index d86b481cd..f056904b5 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -7,6 +7,7 @@ define([ '/common/media-tag.js', '/customize/messages.js', + '/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/croppie/croppie.min.js', '/bower_components/file-saver/FileSaver.min.js', 'css!/bower_components/croppie/croppie.css', @@ -28,7 +29,7 @@ define([ MT.getCursorAvatar = function (cursor) { var html = ''; html += (cursor.avatar && avatars[cursor.avatar]) || ''; - html += cursor.name + ''; + html += Util.fixHTML(cursor.name) + '
'; return html; }; diff --git a/www/common/outer/roster.js b/www/common/outer/roster.js index 162efb21c..d7976c8da 100644 --- a/www/common/outer/roster.js +++ b/www/common/outer/roster.js @@ -481,7 +481,9 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { if (!config.anon_rpc) { return void cb("EXPECTED_ANON_RPC"); } - var response = Util.response(); + var response = Util.response(function (label, info) { + console.error('ROSTER_RESPONSE__' + label, info); + }); var anon_rpc = config.anon_rpc; var keys = config.keys; var me = keys.myCurvePublic; diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index d2d086f47..90ca5e256 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -32,7 +32,9 @@ define([ module.create = function (common, config) { var File = {}; var origin = common.getMetadataMgr().getPrivateData().origin; - var response = Util.response(); + var response = Util.response(function (label, info) { + console.error('COMMON_UPLOAD__' + label, info); + }); var teamId = config.teamId; From e1069b0abb6d64e256b616acd956d104e86d98de Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 6 Apr 2020 20:02:33 -0400 Subject: [PATCH 11/21] 3.15.0 changelog --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c80b2b8f..566758ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +# PigFootedBandicoot release (3.15.0) + +## Goals + +Our plan for this release was to allow our server's code to stabilize after a prologued period of major changes. The massive surge of new users on cryptpad.fr forced us to change our plans and focus instead on increasing performance and scalability of our serverside code and its supporting infrastructure. Most of this release's changes have been thoroughly tested as they've been deployed to our instance on an ongoing basis, however, we're still looking forward to stabilizing as planned. + +We also ended up making significant improvements to our clientside code, since the increased load on the server seemed to exacerbate a few race conditions which occurred less frequently under the previous circumstances. + +## Update notes + +Updating from version 3.14.0 should follow the usual process: + +1. stop your server +2. fetch the latest code with git +3. install clientside dependencies with `bower update` +4. install serverside dependencies with `npm i` +5. start your server + +You may notice that the server now launches a number of child processes named `crypto-worker.js` and `db-worker.js`. These worker processes make use of however many cores your server has available to perform more CPU-intensive tasks in parallel. + +## Features + +* As noted above, the server uses an multi-process architecture and parallelizes more routines. This improvement will be the most noticeable when the server is run on ARM processors which validate cryptographic signatures particularly slowly. +* The admin panel available to instance administrators now displays a list of "Open files". We added this to help us diagnose a "file descriptor leak" which will be described in the _Bug fixes_ section. +* We received a large number of contributions from translators via our [weblate instance](https://weblate.cryptpad.fr/projects/cryptpad/app/). Most notably, Italian is the fourth language to be fully translated with Finnish and Spanish seemingly in line to take the fifth and sixth spots. +* We've addressed some usability issues in our whiteboard app in response to increased interest. Its canvas now automatically resizes according to the size of your screen and the content you've drawn. Unfortunately, we noticed that the "embed image" functionality was imposing some additional strain on our server, so we decided to implement an admittedly arbitrary limit of 1MB on the size of images embedded in whiteboards. We'll consider removing this restriction when we have time to design a more efficient embedding system. +* We've removed the per-user setting which previously allowed registered users to skip the "pad creation screen" which is displayed before creating a document. This setting has not been the default for some time and was not actively tested, so this "feature" is our way of guaranteeing no future regressions in its behaviour. +* As a part of our effort to improve the server's scalability we evaluated which clientside requests could be sent less often. One such request came from the "usage bar" found in users' drives, teams, and settings pages. Previously it would update every 30 seconds no matter what. Now it only updates if that tab is focused. +* Most actions that an administrator can take with regard to a user's account require the "public key" which is used to identify their account. This key is available on the user's settings page, but many users share their profile URL instead. We've added a button to profile pages which copies the user's public key to the clipboard, so now either page will be sufficient. +* We've updated our [mermaidjs](https://mermaid-js.github.io/mermaid/#/) dependency. For those that don't know, Mermaid is a powerful markup syntax for producing a variety of charts. It's integrated into our code editor. This updated version supports GANTT chart tasks with multiple dependencies, pie charts, and a variety of other useful formats. +* We found that in practice our mermaid charts and other embedded media were sufficiently detailed that they became difficult to read on some screens. In response we've added the ability to view these elements in a "lightbox UI" which is nearly full-screen. This interface is can be used to view media contained in the "preview pane" of the code editor as well as within user and team drives, as well as a few other places where Markdown is used. + +## Bug fixes + +This release contains fixes for a lot of bugs. We'll provide a brief overview, but in the interest of putting more time towards development I'll just put my strong recommendation that you update. + +* The server process didn't always close file descriptors that it opened, resulting in an EMFILE error when the system ran out of available file descriptors. Now it closes them. +* The server also kept an unbounded amount of data in an in-memory cache under certain circumstances. Now it doesn't. +* A simple check to ignore the `premiumUploadSize` config value if it was less than `maxUploadSize` incorrectly compared against `defaultStorageLimit`. Premium upload sizes were disabled on our instance when we increased the default storage limit to 1GB. It's fixed now. +* We accepted a [PR](https://github.com/xwiki-labs/cryptpad/pull/513) to prevent a typeError when logging to disk was entirely disabled. +* We identified and fixed the cause of [This issue](https://github.com/xwiki-labs/cryptpad/issues/518) which caused spreadsheets not to load. +* Emojis at the start of users display names were not displayed correctly in the Kanban's "cursor" +* We (once again) believe we've fixed the [duplicated text bug](https://github.com/xwiki-labs/cryptpad/issues/352). Time will tell. +* Our existing Mermaidjs integration supported the special syntax to make elements clickable, but the resulting links don't work within CryptPad. We now remove them. +* Rather than having messages time out if they are not received by the server within a certain timeframe we now wait until the client reconnects, at which point we can check whether those messages exist in the document's history. On a related note we now detect when the realtime system is in a bad state and recreate it. +* Finally, we've fixed a variety of errors in spreadsheets. + # OrienteCaveRat release (3.14.0) ## Goals From e5bcaeedce7be18b40d11ffc6ff0eb8e74864d3a Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 7 Apr 2020 15:38:16 +0200 Subject: [PATCH 12/21] Fix regression in the access modal --- www/common/inner/access.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/inner/access.js b/www/common/inner/access.js index 611b42255..adc5bc15b 100644 --- a/www/common/inner/access.js +++ b/www/common/inner/access.js @@ -309,7 +309,7 @@ define([ called = true; nThen(function (waitFor) { if (!reload) { return; } - Modal.loadMetadata(common, data, waitFor, "owner"); + Modal.loadMetadata(Env, data, waitFor, "owner"); }).nThen(function () { owners = data.owners || []; pending_owners = data.pending_owners || []; @@ -617,7 +617,7 @@ define([ called = true; nThen(function (waitFor) { if (!reload) { return; } - Modal.loadMetadata(common, data, waitFor, "allow"); + Modal.loadMetadata(Env, data, waitFor, "allow"); }).nThen(function () { owners = data.owners || []; restricted = data.restricted || false; From dca2707ae3ae39aa82f07c04ad52e0f212bf8db9 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 7 Apr 2020 09:45:21 -0400 Subject: [PATCH 13/21] guard against incorrect types in /auth/ --- www/auth/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/auth/main.js b/www/auth/main.js index c155204ec..fcbeaf3af 100644 --- a/www/auth/main.js +++ b/www/auth/main.js @@ -71,7 +71,7 @@ define([ // Get contacts and extract their avatar channel and key var getData = function (obj, href) { var parsed = Hash.parsePadUrl(href); - if (!parsed || parsed.type !== "file") { return; } // XXX + if (!parsed || parsed.type !== "file") { return; } var secret = Hash.getSecrets('file', parsed.hash); if (!secret.keys || !secret.channel) { return; } obj.avatarKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey); @@ -81,7 +81,7 @@ define([ contacts.friends = proxy.friends || {}; Object.keys(contacts.friends).map(function (key) { var friend = contacts.friends[key]; - // if (!friend) { return; } // XXX how should this be handled? + if (!friend) { return; } var ret = { edPublic: friend.edPublic, name: friend.displayName, @@ -91,7 +91,7 @@ define([ }); Object.keys(contacts.teams).map(function (key) { var team = contacts.teams[key]; - // if (!team) { return; } // XXX how should this be handled. Is this possible? + if (!team) { return; } var avatar = team.metadata && team.metadata.avatar; var ret = { edPublic: team.keys && team.keys.drive && team.keys.drive.edPublic, From 95965c1deec3a75d506834374f49ed5bbacebee5 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 7 Apr 2020 20:03:41 -0400 Subject: [PATCH 14/21] keep a parallel implementation of the pin loader to validate the new one --- lib/pins.js | 63 ++++++++++++++++++++++++++++++++++ scripts/compare-pin-methods.js | 42 +++++++++++++++++++++++ scripts/evict-inactive.js | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 scripts/compare-pin-methods.js diff --git a/lib/pins.js b/lib/pins.js index 41e871446..d840991a3 100644 --- a/lib/pins.js +++ b/lib/pins.js @@ -7,6 +7,9 @@ const Path = require("path"); const Util = require("./common-util"); const Plan = require("./plan"); +const Semaphore = require('saferphore'); +const nThen = require('nthen'); + /* Accepts a reference to an object, and... either a string describing which log is being processed (backwards compatibility), or a function which will log the error with all relevant data @@ -194,3 +197,63 @@ Pins.list = function (_done, config) { }).start(); }); }; + +Pins.load = function (cb, config) { + const sema = Semaphore.create(config.workers || 5); + + let dirList; + const fileList = []; + const pinned = {}; + + var pinPath = config.pinPath || './pins'; + var done = Util.once(cb); + + nThen((waitFor) => { + // recurse over the configured pinPath, or the default + Fs.readdir(pinPath, waitFor((err, list) => { + if (err) { + if (err.code === 'ENOENT') { + dirList = []; + return; // this ends up calling back with an empty object + } + waitFor.abort(); + return void done(err); + } + dirList = list; + })); + }).nThen((waitFor) => { + dirList.forEach((f) => { + sema.take((returnAfter) => { + // iterate over all the subdirectories in the pin store + Fs.readdir(Path.join(pinPath, f), waitFor(returnAfter((err, list2) => { + if (err) { + waitFor.abort(); + return void done(err); + } + list2.forEach((ff) => { + if (config && config.exclude && config.exclude.indexOf(ff) > -1) { return; } + fileList.push(Path.join(pinPath, f, ff)); + }); + }))); + }); + }); + }).nThen((waitFor) => { + fileList.forEach((f) => { + sema.take((returnAfter) => { + Fs.readFile(f, waitFor(returnAfter((err, content) => { + if (err) { + waitFor.abort(); + return void done(err); + } + const hashes = Pins.calculateFromLog(content.toString('utf8'), f); + hashes.forEach((x) => { + (pinned[x] = pinned[x] || {})[f.replace(/.*\/([^/]*).ndjson$/, (x, y)=>y)] = 1; + }); + }))); + }); + }); + }).nThen(() => { + done(void 0, pinned); + }); +}; + diff --git a/scripts/compare-pin-methods.js b/scripts/compare-pin-methods.js new file mode 100644 index 000000000..de7ef114d --- /dev/null +++ b/scripts/compare-pin-methods.js @@ -0,0 +1,42 @@ +/* jshint esversion: 6, node: true */ +const nThen = require("nthen"); +const Pins = require("../lib/pins"); +const Assert = require("assert"); + +const config = require("../lib/load-config"); + +var compare = function () { + console.log(config); + var conf = { + pinPath: config.pinPath, + }; + + var list, load; + + nThen(function (w) { + Pins.list(w(function (err, p) { + if (err) { throw err; } + list = p; + console.log(p); + console.log(list); + console.log(); + }), conf); + }).nThen(function (w) { + Pins.load(w(function (err, p) { + if (err) { throw err; } + load = p; + console.log(load); + console.log(); + }), conf); + }).nThen(function () { + console.log({ + listLength: Object.keys(list).length, + loadLength: Object.keys(load).length, + }); + + Assert.deepEqual(list, load); + console.log("methods are equivalent"); + }); +}; + +compare(); diff --git a/scripts/evict-inactive.js b/scripts/evict-inactive.js index a3a595ca4..1d7b87e91 100644 --- a/scripts/evict-inactive.js +++ b/scripts/evict-inactive.js @@ -42,7 +42,7 @@ nThen(function (w) { store = _; })); // load the list of pinned files so you know which files // should not be archived or deleted - Pins.list(w(function (err, _) { + Pins.load(w(function (err, _) { if (err) { w.abort(); return void console.error(err); From 7fac997e93236312ddb68471b858d5242b060044 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 8 Apr 2020 09:38:48 -0400 Subject: [PATCH 15/21] increase some file storage timeouts related to streams --- lib/storage/file.js | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index 2d27ce185..00222c3f6 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -14,6 +14,28 @@ const readFileBin = require("../stream-file").readFileBin; const BatchRead = require("../batch-read"); const Schedule = require("../schedule"); + +/* Each time you write to a channel it will either use an open file descriptor + for that channel or open a new descriptor if one is not available. These are + automatically closed after this window to prevent a file descriptor leak, so + writes that take longer than this time may be dropped! */ +const CHANNEL_WRITE_WINDOW = 300000; + +/* Each time you read a channel it will have this many milliseconds to complete + otherwise it will be closed to prevent a file descriptor leak. The server will + lock up if it uses all available file descriptors, so it's important to close + them. The tradeoff with this timeout is that some functions, the stream, and + and the timeout itself are stored in memory. A longer timeout uses more memory + and running out of memory will also kill the server. */ +const STREAM_CLOSE_TIMEOUT = 300000; + +/* The above timeout closes the stream, but apparently that doesn't always work. + We set yet another timeout to allow the runtime to gracefully close the stream + (flushing all pending writes/reads and doing who knows what else). After this timeout + it will be MERCILESSLY DESTROYED. This isn't graceful, but again, file descriptor + leaks are bad. */ +const STREAM_DESTROY_TIMEOUT = 30000; + const isValidChannelId = function (id) { return typeof(id) === 'string' && id.length >= 32 && id.length < 50 && @@ -64,7 +86,7 @@ const destroyStream = function (stream) { try { stream.close(); } catch (err) { console.error(err); } setTimeout(function () { try { stream.destroy(); } catch (err) { console.error(err); } - }, 15000); + }, STREAM_DESTROY_TIMEOUT); }; const ensureStreamCloses = function (stream, id, ms) { @@ -74,7 +96,7 @@ const ensureStreamCloses = function (stream, id, ms) { // this can only be a timeout error... console.log("stream close error:", err, id); } - }), ms || 45000), []); + }), ms || STREAM_CLOSE_TIMEOUT), []); }; // readMessagesBin asynchronously iterates over the messages in a channel log @@ -729,7 +751,7 @@ var getChannel = function (env, id, _callback) { delete env.channels[id]; destroyStream(channel.writeStream, path); //console.log("closing writestream"); - }, 120000); + }, CHANNEL_WRITE_WINDOW); channel.delayClose(); env.channels[id] = channel; done(void 0, channel); From bef18a93201adb4bd067551504616f7407180865 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 8 Apr 2020 10:28:26 -0400 Subject: [PATCH 16/21] hotfix --- lib/commands/pin-rpc.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/commands/pin-rpc.js b/lib/commands/pin-rpc.js index f3ade0489..258c6f53b 100644 --- a/lib/commands/pin-rpc.js +++ b/lib/commands/pin-rpc.js @@ -56,9 +56,13 @@ var loadUserPins = function (Env, safeKey, cb) { }; var truthyKeys = function (O) { - return Object.keys(O).filter(function (k) { - return O[k]; - }); + try { + return Object.keys(O).filter(function (k) { + return O[k]; + }); + } catch (err) { + return []; + } }; var getChannelList = Pinning.getChannelList = function (Env, safeKey, _cb) { From bd3e18d1a87d93caf63faeca7e81f3bfe0527758 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 8 Apr 2020 12:08:26 -0400 Subject: [PATCH 17/21] only cache a user's pinned channels if there are no errors when loading them --- lib/commands/pin-rpc.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/commands/pin-rpc.js b/lib/commands/pin-rpc.js index 258c6f53b..a6f1642c6 100644 --- a/lib/commands/pin-rpc.js +++ b/lib/commands/pin-rpc.js @@ -49,7 +49,6 @@ var loadUserPins = function (Env, safeKey, cb) { // only put this into the cache if it completes session.channels = value; } - session.channels = value; done(value); }); }); From ebd4998edd2ba024daf146503f118ff8da9e9875 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 9 Apr 2020 19:08:12 +0200 Subject: [PATCH 18/21] Translated using Weblate (Catalan) Currently translated at 50.8% (633 of 1246 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ca/ --- www/common/translations/messages.ca.json | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/www/common/translations/messages.ca.json b/www/common/translations/messages.ca.json index aca3b8068..a10a9fffd 100644 --- a/www/common/translations/messages.ca.json +++ b/www/common/translations/messages.ca.json @@ -350,7 +350,7 @@ "fm_info_root": "Creeu aquí tantes carpetes imbrincades com vulgueu per ordenar els vostres fitxers.", "fm_info_unsorted": "Conté tots els fitxers que heu visitat i que encara no estan ordenats, a \"Documents\" o desplaçats a la \"Paperera\".", "fm_info_template": "Conté tots els documents desats com plantilles i que podeu reutilitzar quan vulgueu crear un nou document.", - "fm_info_recent": "Llista els documents modificats o oberts recentment.", + "fm_info_recent": "Aquests documents s'han modificat o obert darrerament, per vós o per alguna persona col·laboradora.", "fm_info_trash": "Buideu la paperera per alliberar espai al vostre CryptDrive.", "fm_info_allFiles": "Conté tots els fitxers de \"Documents\", \"Desordenats\" i \"Paperera\". No podeu desplaçar o suprimir fitxers des d'aquí.", "fm_info_anonymous": "No heu iniciat la sessió, per tant, els vostres documents caducaran d'aquí a 3 mesos (saber-ne més). Es desen al vostre navegador, per tant, si netegeu el vostre historial podríeu perdre'ls.
Registreu-vos o Inicieu la sessió per mantenir-los accessibles.
", @@ -515,7 +515,7 @@ "settings_pinningError": "Alguna cosa no ha funcionat correctament", "settings_usageAmount": "Els vostres documents fixats ocupen {0} MB", "settings_logoutEverywhereButton": "Tanca la sessió", - "settings_logoutEverywhereTitle": "Tanca la sessió arreu", + "settings_logoutEverywhereTitle": "Tanca les sessions remotes", "settings_logoutEverywhere": "Tanca totes les altres sessions", "settings_logoutEverywhereConfirm": "De debò? Haureu de tornar a iniciar la vostra sessió a tots els dispositius.", "settings_driveDuplicateTitle": "Documents propis duplicats", @@ -573,7 +573,7 @@ "upload_success": "El fitxer ({0}) ha estat carregat correctament i afegit al vostre CryptDrive.", "upload_notEnoughSpace": "No hi ha prou espai al CryptDrive per aquest fitxer.", "upload_notEnoughSpaceBrief": "No hi ha prou espai", - "upload_tooLarge": "Aquest fitxer supera la mida màxima permesa.", + "upload_tooLarge": "Aquest fitxer supera la mida màxima permesa pel vostre compte", "upload_tooLargeBrief": "El fitxer és massa gran", "upload_choose": "Trieu un fitxer", "upload_pending": "Pendent", @@ -619,5 +619,20 @@ "download_resourceNotAvailable": "El recurs sol·licitat no estava disponible... Premeu Esc per continuar.", "about_contributors": "Col·laboracions clau", "about_core": "Desenvolupament principal", - "about_intro": "CryptPad s'ha creat dins l'Equip de Recerca de XWiki SAS, una petita empresa de París, França i Iasi, Romania. Hi ha 3 membres de l'equip central treballant amb CryptPad més una quantitat de persones col·laboradores, dins i fora d'XWiki SAS." + "about_intro": "CryptPad s'ha creat dins l'Equip de Recerca de XWiki SAS, una petita empresa de París, França i Iasi, Romania. Hi ha 3 membres de l'equip central treballant amb CryptPad més una quantitat de persones col·laboradores, dins i fora d'XWiki SAS.", + "main_catch_phrase": "El Núvol Coneixement Zero", + "main_footerText": "Amb Cryptpad, podeu crear documents col·laboratius per prendre notes i posar en ordre idees de forma conjunta de forma ràpida.", + "footer_applications": "Aplicacions", + "footer_contact": "Contacte", + "footer_aboutUs": "Sobre nosaltres", + "about": "Sobre", + "contact": "Contacte", + "blog": "Bloc", + "topbar_whatIsCryptpad": "Què és CryptPad", + "whatis_collaboration": "Col·laboració fàcil i ràpida", + "whatis_title": "Què és CryptPad", + "terms": "Condicions d'ús", + "main_info": "

Col·laboreu amb Confiança

\nFeu créixer les vostres idees conjuntament amb documents compartits mentre la tecnologia Coneixement Zero assegura la vostra privacitat; fins i tot per nosaltres.", + "whatis_collaboration_p1": "Amb CryptPad, podeu crear de forma ràpida, documents col·laboratius per prendre notes i posar en ordre idees conjuntament. Quan us registreu i inicieu la vostra sessió, teniu la capacitat de carregar fitxers i un CryptDrive on podeu organitzar tots els vostres documents. Com a persona registrada disposeu de 50MB d'espai gratuït.", + "privacy": "Privacitat" } From e527867e2ea763d674803768fab65f644f39dcf6 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 9 Apr 2020 16:29:52 -0400 Subject: [PATCH 19/21] queue blob and channel deletions per-user --- lib/commands/channel.js | 28 +++++++++++++++++++--------- lib/historyKeeper.js | 1 + 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/commands/channel.js b/lib/commands/channel.js index d35e15241..fce5b048c 100644 --- a/lib/commands/channel.js +++ b/lib/commands/channel.js @@ -54,16 +54,8 @@ Channel.clearOwnedChannel = function (Env, safeKey, channelId, cb, Server) { }); }; -Channel.removeOwnedChannel = function (Env, safeKey, channelId, cb, Server) { - if (typeof(channelId) !== 'string' || !Core.isValidId(channelId)) { - return cb('INVALID_ARGUMENTS'); - } +var archiveOwnedChannel = function (Env, safeKey, channelId, cb, Server) { var unsafeKey = Util.unescapeKeyCharacters(safeKey); - - if (Env.blobStore.isFileId(channelId)) { - return void Env.removeOwnedBlob(channelId, safeKey, cb); - } - Metadata.getMetadata(Env, channelId, function (err, metadata) { if (err) { return void cb(err); } if (!Core.hasOwners(metadata)) { return void cb('E_NO_OWNERS'); } @@ -124,6 +116,24 @@ Channel.removeOwnedChannel = function (Env, safeKey, channelId, cb, Server) { }); }; +Channel.removeOwnedChannel = function (Env, safeKey, channelId, __cb, Server) { + var _cb = Util.once(Util.mkAsync(__cb)); + + if (typeof(channelId) !== 'string' || !Core.isValidId(channelId)) { + return _cb('INVALID_ARGUMENTS'); + } + + // archiving large channels or files can be expensive, so do it one at a time + // for any given user to ensure that nobody can use too much of the server's resources + Env.queueDeletes(safeKey, function (next) { + var cb = Util.both(_cb, next); + if (Env.blobStore.isFileId(channelId)) { + return void Env.removeOwnedBlob(channelId, safeKey, cb); + } + archiveOwnedChannel(Env, safeKey, channelId, cb, Server); + }); +}; + Channel.trimHistory = function (Env, safeKey, data, cb) { if (!(data && typeof(data.channel) === 'string' && typeof(data.hash) === 'string' && data.hash.length === 64)) { return void cb('INVALID_ARGS'); diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index f70dba006..2b5ec8770 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -38,6 +38,7 @@ module.exports.create = function (config, cb) { metadata_cache: {}, channel_cache: {}, queueStorage: WriteQueue(), + queueDeletes: WriteQueue(), batchIndexReads: BatchRead("HK_GET_INDEX"), batchMetadata: BatchRead('GET_METADATA'), From 09bf0a54d81ca2fe651d22df0fc60c8c5b1c2d4a Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 9 Apr 2020 16:30:22 -0400 Subject: [PATCH 20/21] add access control header --- docs/example.nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index ea8224c14..f6e163910 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -54,6 +54,7 @@ server { add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; + add_header Access-Control-Allow-Origin "*"; # add_header X-Frame-Options "SAMEORIGIN"; # Insert the path to your CryptPad repository root here From de6594f4c9a1155d0c7ee0b2aead713f99b20930 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 9 Apr 2020 16:31:16 -0400 Subject: [PATCH 21/21] add some XXX notes --- www/common/onlyoffice/inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 5ac3da269..3a824214c 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1091,12 +1091,12 @@ define([ var x2tSaveAndConvertData = function(data, filename, extension, finalFilename) { // Perform the x2t conversion - require(['/common/onlyoffice/x2t/x2t.js'], function() { + require(['/common/onlyoffice/x2t/x2t.js'], function() { // XXX why does this fail without an access-control-allow-origin header? var x2t = window.Module; x2t.run(); if (x2tInitialized) { debug("x2t runtime already initialized"); - x2tSaveAndConvertDataInternal(x2t, data, filename, extension, finalFilename); + x2tSaveAndConvertDataInternal(x2t, data, filename, extension, finalFilename); // XXX shouldn't this return ? } x2t.onRuntimeInitialized = function() {