Merge branch 'staging' into cba

This commit is contained in:
yflory 2020-04-20 15:38:47 +02:00
commit b208ca367d
40 changed files with 369 additions and 106 deletions

View file

@ -11,6 +11,8 @@ CKEDITOR.editorConfig = function( config ) {
config.removePlugins= 'resize,elementspath';
config.resize_enabled= false; //bottom-bar
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64,mathjax,wordcount';
// FIXME translation for default? updating to a newer CKEditor seems like it will add 'default' by default
config.fontSize_sizes = '(Default)/unset;8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px';
config.toolbarGroups= [
// {"name":"clipboard","groups":["clipboard","undo"]},
//{"name":"editing","groups":["find","selection"]},

View file

@ -107,7 +107,7 @@ define([
])*/
])
]),
h('div.cp-version-footer', "CryptPad v3.15.0 (PigFootedBandicoot)")
h('div.cp-version-footer', "CryptPad v3.16.0 (Quagga)")
]);
};

View file

@ -1179,6 +1179,7 @@
&.fa-download { order: 2; }
&.fa-upload { order: 3; }
&.fa-print { order: 4; }
&.fa-arrows-h { order: 5; }
&.fa-cog { order: 5; }
&.fa-info-circle { order: 6; }
&.fa-help { order: 7; }

View file

@ -259,7 +259,7 @@ module.exports.create = function (config, cb) {
throw new Error(err);
}
}));
}).nThen(function (w) {
}).nThen(function () {
if (config.disableIntegratedTasks) { return; }
config.intervals = config.intervals || {};

View file

@ -665,31 +665,17 @@ const handleGetHistoryRange = function (Env, Server, seq, userId, parsed) {
}
Server.send(userId, [seq, 'ACK']);
Env.getOlderHistory(channelName, oldestKnownHash, function (err, messages) {
Env.getOlderHistory(channelName, oldestKnownHash, desiredMessages, desiredCheckpoint, function (err, toSend) {
if (err && err.code !== 'ENOENT') {
Env.Log.error("HK_GET_OLDER_HISTORY", err);
}
if (!Array.isArray(messages)) { messages = []; }
// FIXME this reduction could be done in the worker instead of the main process
var toSend = [];
if (typeof (desiredMessages) === "number") {
toSend = messages.slice(-desiredMessages);
} else {
let cpCount = 0;
for (var i = messages.length - 1; i >= 0; i--) {
if (/^cp\|/.test(messages[i][4]) && i !== (messages.length - 1)) {
cpCount++;
}
toSend.unshift(messages[i]);
if (cpCount >= desiredCheckpoint) { break; }
}
if (Array.isArray(toSend)) {
toSend.forEach(function (msg) {
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId,
JSON.stringify(['HISTORY_RANGE', txid, msg])]);
});
}
toSend.forEach(function (msg) {
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId,
JSON.stringify(['HISTORY_RANGE', txid, msg])]);
});
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId,
JSON.stringify(['HISTORY_RANGE_END', txid, channelName])

View file

@ -27,7 +27,7 @@ const CHANNEL_WRITE_WINDOW = 300000;
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;
const STREAM_CLOSE_TIMEOUT = 120000;
/* 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
@ -83,15 +83,31 @@ var channelExists = function (filepath, cb) {
const destroyStream = function (stream) {
if (!stream) { return; }
try { stream.close(); } catch (err) { console.error(err); }
try {
stream.close();
if (stream.closed && stream.fd === null) { return; }
} catch (err) {
console.error(err);
}
setTimeout(function () {
try { stream.destroy(); } catch (err) { console.error(err); }
}, STREAM_DESTROY_TIMEOUT);
};
/*
accept a stream, an id (used as a label) and an optional number of milliseconds
return a function which ignores all arguments
and first tries to gracefully close a stream
then destroys it after a period if the close was not successful
if the function is not called within the specified number of milliseconds
then it will be called implicitly with an error to indicate
that it was run because it timed out
*/
const ensureStreamCloses = function (stream, id, ms) {
return Util.bake(Util.mkTimeout(Util.once(function (err) {
destroyStream(stream, id);
destroyStream(stream);
if (err) {
// this can only be a timeout error...
console.log("stream close error:", err, id);
@ -106,7 +122,7 @@ const ensureStreamCloses = function (stream, id, ms) {
// it also allows the handler to abort reading at any time
const readMessagesBin = (env, id, start, msgHandler, cb) => {
const stream = Fs.createReadStream(mkPath(env, id), { start: start });
const finish = ensureStreamCloses(stream, id);
const finish = ensureStreamCloses(stream, '[readMessagesBin:' + id + ']');
return void readFileBin(stream, msgHandler, function (err) {
cb(err);
finish();
@ -117,7 +133,7 @@ const readMessagesBin = (env, id, start, msgHandler, cb) => {
// returns undefined if the first message was not an object (not an array)
var getMetadataAtPath = function (Env, path, _cb) {
const stream = Fs.createReadStream(path, { start: 0 });
const finish = ensureStreamCloses(stream, path);
const finish = ensureStreamCloses(stream, '[getMetadataAtPath:' + path + ']');
var cb = Util.once(Util.mkAsync(Util.both(_cb, finish)), function () {
throw new Error("Multiple Callbacks");
});
@ -203,7 +219,7 @@ var clearChannel = function (env, channelId, _cb) {
*/
var readMessages = function (path, msgHandler, _cb) {
var stream = Fs.createReadStream(path, { start: 0});
const finish = ensureStreamCloses(stream, path);
const finish = ensureStreamCloses(stream, '[readMessages:' + path + ']');
var cb = Util.once(Util.mkAsync(Util.both(finish, _cb)));
return readFileBin(stream, function (msgObj, readMore) {
@ -231,7 +247,7 @@ var getDedicatedMetadata = function (env, channelId, handler, _cb) {
var metadataPath = mkMetadataPath(env, channelId);
var stream = Fs.createReadStream(metadataPath, {start: 0});
const finish = ensureStreamCloses(stream, metadataPath);
const finish = ensureStreamCloses(stream, '[getDedicatedMetadata:' + metadataPath + ']');
var cb = Util.both(finish, _cb);
readFileBin(stream, function (msgObj, readMore) {

View file

@ -222,10 +222,10 @@ const computeMetadata = function (data, cb) {
const getOlderHistory = function (data, cb) {
const oldestKnownHash = data.hash;
const channelName = data.channel;
const desiredMessages = data.desiredMessages;
const desiredCheckpoint = data.desiredCheckpoint;
//const store = Env.store;
//const Log = Env.Log;
var messageBuffer = [];
var messages = [];
var found = false;
store.getMessages(channelName, function (msgStr) {
if (found) { return; }
@ -246,9 +246,22 @@ const getOlderHistory = function (data, cb) {
if (hash === oldestKnownHash) {
found = true;
}
messageBuffer.push(parsed);
messages.push(parsed);
}, function (err) {
cb(err, messageBuffer);
var toSend = [];
if (typeof (desiredMessages) === "number") {
toSend = messages.slice(-desiredMessages);
} else {
let cpCount = 0;
for (var i = messages.length - 1; i >= 0; i--) {
if (/^cp\|/.test(messages[i][4]) && i !== (messages.length - 1)) {
cpCount++;
}
toSend.unshift(messages[i]);
if (cpCount >= desiredCheckpoint) { break; }
}
}
cb(err, toSend);
});
};

View file

@ -250,12 +250,14 @@ Workers.initialize = function (Env, config, _cb) {
});
};
Env.getOlderHistory = function (channel, oldestKnownHash, cb) {
Env.getOlderHistory = function (channel, oldestKnownHash, desiredMessages, desiredCheckpoint, cb) {
Env.store.getWeakLock(channel, function (next) {
sendCommand({
channel: channel,
command: "GET_OLDER_HISTORY",
hash: oldestKnownHash,
desiredMessages: desiredMessages,
desiredCheckpoint: desiredCheckpoint,
}, Util.both(next, cb));
});
};

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "cryptpad",
"version": "3.15.0",
"version": "3.16.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.15.0",
"version": "3.16.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -1651,7 +1651,6 @@ define([
if (data.hiddenReadOnly) { button.addClass('cp-hidden-if-readonly'); }
if (data.name) {
button.addClass('cp-toolbar-icon-'+data.name);
button.click(common.prepareFeedback(data.name));
}
if (data.text) {
$('<span>', {'class': 'cp-toolbar-drawer-element'}).text(data.text)

View file

@ -313,6 +313,15 @@ define([
// finally, find all 'clickable' items and remove the class
$el.find('.clickable').removeClass('clickable');
};
var renderMermaid = function ($el) {
Mermaid.init(undefined, $el);
// clickable elements in mermaid don't work well with our sandboxing setup
// the function below strips clickable elements but still leaves behind some artifacts
// tippy tooltips might still be useful, so they're not removed. It would be
// preferable to just support links, but this covers up a rough edge in the meantime
removeMermaidClickables($el);
};
DiffMd.apply = function (newHtml, $content, common) {
var contextMenu = common.importMediaTagMenu();
@ -387,8 +396,15 @@ define([
var mts = [];
$content.find('media-tag, pre.mermaid').each(function (i, el) {
if (el.nodeName.toLowerCase() === "pre") {
var clone = el.cloneNode();
return void mts.push({
svg: el.cloneNode(true)
svg: clone,
render: function () {
var $el = $(clone);
$el.text(clone.getAttribute('mermaid-source'));
$el.attr('data-processed', '');
renderMermaid($el);
}
});
}
var $el = $(el);
@ -401,7 +417,7 @@ define([
// Find initial position
var idx = -1;
mts.some(function (obj, i) {
if (isSvg && $mt.find('svg').attr('id') === $(obj.svg).find('svg').attr('id')) {
if (isSvg && $mt.attr('mermaid-source') === $(obj.svg).attr('mermaid-source')) {
idx = i;
return true;
}
@ -412,8 +428,15 @@ define([
});
if (idx === -1) {
if (isSvg) {
var clone = $mt[0].cloneNode();
mts.unshift({
svg: $mt[0].cloneNode(true)
svg: clone,
render: function () {
var $el = $(clone);
$el.text(clone.getAttribute('mermaid-source'));
$el.attr('data-processed', '');
renderMermaid($el);
}
});
} else {
mts.unshift({
@ -511,12 +534,7 @@ define([
// check if you had cached a pre-rendered instance of the supplied source
if (typeof(cached) !== 'object') {
try {
Mermaid.init(undefined, $el);
// clickable elements in mermaid don't work well with our sandboxing setup
// the function below strips clickable elements but still leaves behind some artifacts
// tippy tooltips might still be useful, so they're not removed. It would be
// preferable to just support links, but this covers up a rough edge in the meantime
removeMermaidClickables($el);
renderMermaid($el);
} catch (e) { console.error(e); }
return;
}

View file

@ -250,36 +250,47 @@ define([
// Check src and cryptkey
var cfg = tags[i];
var tag;
if (cfg.svg) {
$spinner.hide();
$inner.append(cfg.svg);
locked = false;
return;
if (!cfg.render) {
$spinner.hide();
console.error('here');
locked = false;
return;
}
console.error('there');
setTimeout(cfg.render);
tag = cfg.svg;
} else {
var src = cfg.src;
var key = cfg.key;
if (cfg.href) {
var parsed = Hash.parsePadUrl(cfg.href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, cfg.password);
var host = priv.fileHost || priv.origin || '';
src = host + Hash.getBlobPathFromHex(secret.channel);
var _key = secret.keys && secret.keys.cryptKey;
if (_key) { key = 'cryptpad:' + Nacl.util.encodeBase64(_key); }
}
if (!src || !key) {
locked = false;
$spinner.hide();
return void UI.log(Messages.error);
}
tag = h('media-tag', {
src: src,
'data-crypto-key': key
});
$inner.append(tag);
MediaTag(tag).on('error', function () {
locked = false;
$spinner.hide();
UI.log(Messages.error);
});
}
var src = cfg.src;
var key = cfg.key;
if (cfg.href) {
var parsed = Hash.parsePadUrl(cfg.href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, cfg.password);
var host = priv.fileHost || priv.origin || '';
src = host + Hash.getBlobPathFromHex(secret.channel);
var _key = secret.keys && secret.keys.cryptKey;
if (_key) { key = 'cryptpad:' + Nacl.util.encodeBase64(_key); }
}
if (!src || !key) {
locked = false;
$spinner.hide();
return void UI.log(Messages.error);
}
var tag = h('media-tag', {
src: src,
'data-crypto-key': key
});
$inner.append(tag);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function() {
locked = false;
@ -291,11 +302,6 @@ define([
childList: true,
characterData: false
});
MediaTag(tag).on('error', function () {
locked = false;
$spinner.hide();
UI.log(Messages.error);
});
};
show(i);

View file

@ -355,7 +355,6 @@ define([
APP.FM.handleFile(blob, data);
};
Messages.oo_login = 'Log in...'; // XXX
var noLogin = false;
var makeCheckpoint = function (force) {
@ -1120,12 +1119,12 @@ define([
var x2tSaveAndConvertData = function(data, filename, extension, finalFilename) {
// Perform the x2t conversion
require(['/common/onlyoffice/x2t/x2t.js'], function() { // XXX why does this fail without an access-control-allow-origin header?
require(['/common/onlyoffice/x2t/x2t.js'], function() { // FIXME 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); // XXX shouldn't this return ?
return void x2tSaveAndConvertDataInternal(x2t, data, filename, extension, finalFilename);
}
x2t.onRuntimeInitialized = function() {

View file

@ -38,6 +38,9 @@ define([
drive: {
hideDuplicate: true
},
pad: {
width: true
},
general: {
allowUserFeedback: true
}

View file

@ -1342,5 +1342,13 @@
"admin_openFilesTitle": "Offene Dateien",
"canvas_select": "Auswahl",
"canvas_brush": "Pinsel",
"profile_copyKey": "Öffentlichen Schlüssel kopieren"
"profile_copyKey": "Öffentlichen Schlüssel kopieren",
"cba_hide": "Autorenfarben verbergen",
"cba_enable": "Aktivieren",
"cba_properties": "Autorenfarben (experimentell)",
"cba_disable": "Löschen und Deaktivieren",
"cba_show": "Autorenfarben anzeigen",
"cba_writtenBy": "Geschrieben von: {0}",
"cba_hint": "Diese Einstellung wird zukünftig verwendet, wenn du ein neues Pad erstellst.",
"oo_login": "Bitte logge dich ein oder erstelle ein Account, um die Performance von Tabellen zu verbessern."
}

View file

@ -1342,5 +1342,13 @@
"admin_openFilesTitle": "Fichiers Ouverts",
"profile_copyKey": "Copier la clé publique",
"canvas_select": "Selection",
"canvas_brush": "Pinceau"
"canvas_brush": "Pinceau",
"cba_show": "Montrer les couleurs d'auteurs",
"cba_disable": "Effacer et Désactiver",
"cba_hint": "Ce réglage sera mémorisé lors de la création de votre prochain pad.",
"cba_enable": "Activer",
"cba_writtenBy": "Écrit par : {0}",
"cba_properties": "Couleurs par auteurs (expérimental)",
"cba_hide": "Cacher les couleurs d'auteurs",
"oo_login": "Veuillez vous connecter ou vous inscrire pour améliorer la performance des feuilles de calcul."
}

View file

@ -1342,5 +1342,13 @@
"admin_openFilesTitle": "Open Files",
"admin_openFilesHint": "Number of file descriptors currently open on the server.",
"canvas_brush": "Brush",
"canvas_select": "Select"
"canvas_select": "Select",
"cba_writtenBy": "Written by: {0}",
"cba_properties": "Author colors (experimental)",
"cba_hint": "This setting will be remembered when you create your next pad.",
"cba_enable": "Enable",
"cba_disable": "Clear and Disable",
"cba_show": "Show author colors",
"cba_hide": "Hide author colors",
"oo_login": "Please log in or register to improve the performance of spreadsheets."
}

View file

@ -201,6 +201,180 @@
"chainpadError": "Er was een kritieke fout bij het updaten van uw inhoud. Deze pagina is nu alleen leesbaar om uw werk niet kwijt te raken. <br>Druk op <em>Esc</em> om verder te gaan met het bekijken van deze werkomgeving, of herlaad de pagina om te proberen deze werkomgeving weer aan te kunnen passen.",
"inactiveError": "Deze werkomgeving is verwijderd wegens gebrek aan activiteit. Druk op Esc om een nieuwe werkomgeving aan te maken.",
"deletedError": "Deze werkomgeving is verwijderd door de eigenaar en is niet meer beschikbaar.",
"main_title": "CryptPad: Geen Kennis, Onvertraagd Collaboratief Aanpassen"
"main_title": "CryptPad: Geen Kennis, Onvertraagd Collaboratief Aanpassen",
"fc_open_ro": "Openen (alleen-lezen)",
"history_restorePrompt": "Weet u zeker dat u de bestaande documentversie wilt vervangen met de weergegeven versie?",
"history_restoreDone": "Document hersteld",
"history_version": "Versie:",
"openLinkInNewTab": "Open Link in Nieuw Tabblad",
"pad_mediatagTitle": "Mediamarkering Instellingen",
"pad_mediatagWidth": "Breedte (px)",
"pad_mediatagHeight": "Hoogte (px)",
"pad_mediatagRatio": "Behoud ratio",
"pad_mediatagBorder": "Randbreedte (px)",
"pad_mediatagPreview": "Voorbeeld",
"pad_mediatagImport": "Opslaan in uw CryptDrive",
"pad_mediatagOptions": "Afbeeldingeigenschappen",
"kanban_newBoard": "Nieuw bord",
"kanban_item": "Item {0}",
"kanban_todo": "Te Doen",
"kanban_working": "In voortgang",
"kanban_deleteBoard": "Weet u zeker dat u dit bord wilt verwijderen?",
"kanban_addBoard": "Een bord toevoegen",
"kanban_removeItem": "Verwijder dit item",
"kanban_removeItemConfirm": "Weet u zeker dat u dit item wilt verwijderen?",
"poll_p_save": "Uw instellingen worden direct geüpdate, dus u hoeft deze nooit op te slaan.",
"poll_p_encryption": "Al uw invoer worden versleuteld zodat alleen de personen met de link toegang kunnen krijgen. Zelfs de server kan niet zien wat u wijzigt.",
"wizardLog": "Klik op de knop linksboven om terug te gaan naar uw enquête",
"wizardTitle": "Gebruik het formulier om een enquête aan te maken",
"wizardConfirm": "Bent u echt klaar om deze opties toe te voegen aan uw enquête?",
"poll_publish_button": "Publiceren",
"poll_admin_button": "Admin",
"poll_create_user": "Voeg een nieuwe gebruiker toe",
"poll_create_option": "Voeg een nieuwe optie toe",
"poll_commit": "Indienen",
"poll_closeWizardButton": "Sluit formulier",
"poll_closeWizardButtonTitle": "Sluit formulier",
"poll_wizardComputeButton": "Bereken Opties",
"poll_wizardClearButton": "Tabel Leegmaken",
"poll_wizardAddDateButton": "+ Data",
"poll_wizardAddTimeButton": "+ Tijden",
"poll_optionPlaceholder": "Optie",
"poll_userPlaceholder": "Uw naam",
"poll_removeOption": "Weet u zeker dat u deze optie wilt verwijderen?",
"poll_removeUser": "Weet u zeker dat u deze gebruiker wilt verwijderen?",
"poll_titleHint": "Titel",
"poll_remove": "Verwijder",
"poll_edit": "Wijzig",
"poll_locked": "Vergrendeld",
"poll_unlocked": "Ontgrendeld",
"poll_bookmark_col": "Voeg een bladwijzer toe aan deze kolom zodat deze altijd ontgrendeld is en weergegeven aan het begin",
"poll_bookmarked_col": "Dit is uw kolom met bladwijzer. Deze is altijd ontgrendeld en weergegeven aan het begin.",
"poll_total": "TOTAAL",
"poll_comment_list": "Commentaar",
"poll_comment_add": "Commentaar toevoegen",
"poll_comment_submit": "Verzenden",
"poll_comment_placeholder": "Uw commentaar",
"poll_comment_disabled": "Publiceer deze enquête met de ✓ knop om commentaar toe te staan.",
"oo_reconnect": "De verbinding met de server is terug. Klik op OK om te herladen en door te gaan met werken.",
"oo_cantUpload": "Uploaden is niet toegestaan als andere gebruikers aanwezig zijn.",
"oo_uploaded": "Uw upload is voltooid. Klik op OK om te herladen of Annuleren om door te gaan in alleen-lezen modus.",
"canvas_clear": "Leegmaken",
"canvas_delete": "Verwijder selectie",
"canvas_disable": "Tekenen uitschakelen",
"canvas_enable": "Tekenen inschakelen",
"canvas_width": "Breedte",
"canvas_opacity": "Ondoorzichtigheid",
"canvas_opacityLabel": "Ondoorzichtigheid: {0}",
"canvas_widthLabel": "Breedte: {0}",
"canvas_saveToDrive": "Deze afbeelding opslaan als bestand in uw CryptDrive",
"canvas_currentBrush": "Huidige borstel",
"canvas_chooseColor": "Kies een kleur",
"canvas_imageEmbed": "Plaats een afbeelding van uw computer",
"profileButton": "Profiel",
"profile_urlPlaceholder": "URL",
"profile_namePlaceholder": "Naam weergegeven op uw profiel",
"profile_avatar": "Avatar",
"profile_upload": " Een nieuw avatar uploaden",
"profile_uploadSizeError": "Foutmelding: uw avatar moet kleiner zijn dan {0}",
"profile_uploadTypeError": "Foutmelding: uw avatartype is niet toegestaan. Toegestane types zijn: {0}",
"profile_error": "Foutmelding tijdens het maken van uw profiel: {0}",
"profile_register": "U moet zich aanmelden om een profiel aan te maken!",
"profile_create": "Een profiel aanmaken",
"profile_description": "Beschrijving",
"profile_viewMyProfile": "Mijn profiel bekijken",
"userlist_addAsFriendTitle": "Stuur \"{0}\" een contactverzoek",
"contacts_title": "Contacten",
"contacts_addError": "Foutmelding tijdens het toevoegen van die contact aan de lijst",
"contacts_added": "Contactuitnodiging geaccepteerd.",
"contacts_rejected": "Contactuitnodiging afgewezen",
"contacts_request": "<em>{0}</em> wilt u toevoegen als contactpersoon. <b>Accepteren<b>?",
"contacts_send": "Verzenden",
"contacts_remove": "Verwijder deze contactpersoon",
"contacts_confirmRemove": "Weet u zeker dat u <em>{0}</em> wilt verwijderen uit uw contacten?",
"contacts_typeHere": "Schrijf hier een bericht...",
"contacts_warning": "Alles dat u hier schrijft blijft beschikbaar voor alle huidige en toekomstige gebruikers van deze werkomgeving. Wees voorzichtig met gevoelige informatie!",
"contacts_padTitle": "Gesprek",
"contacts_info1": "Dit zijn uw contacten. Vanaf hier kunt u:",
"contacts_info2": "Klik op uw contactpersoon's icoon om een gesprek met hun te beginnen",
"contacts_info3": "Dubbelklik op hun icoon om hun profiel te bekijken",
"contacts_info4": "Beide deelnemers kunnen de gesprekgeschiedenis voorgoed leegmaken",
"contacts_removeHistoryTitle": "Gesprekgeschiedenis leegmaken",
"contacts_confirmRemoveHistory": "Weet u zeker dat u voorgoed de gesprekgeschiedenis wilt leegmaken? De data kan niet worden hersteld",
"contacts_removeHistoryServerError": "Foutmelding tijdens het verwijderen van uw gesprekgeschiedenis. Probeer later opnieuw",
"contacts_fetchHistory": "Eerdere geschiedenis binnenhalen",
"contacts_friends": "Contacten",
"contacts_rooms": "Kamers",
"contacts_leaveRoom": "Deze kamer verlaten",
"contacts_online": "Een andere gebruiker in deze kamer is online",
"debug_getGraphWait": "De grafiek genereren... Even wachten, alstublieft.",
"fm_rootName": "Documenten",
"fm_trashName": "Prullenbak",
"fm_unsortedName": "Niet-gesorteerde bestanden",
"fm_filesDataName": "Alle bestanden",
"fm_templateName": "Sjablonen",
"fm_searchName": "Zoeken",
"fm_recentPadsName": "Recente werkomgevingen",
"fm_tagsName": "Markeringen",
"fm_sharedFolderName": "Gedeelde map",
"fm_searchPlaceholder": "Zoeken...",
"fm_newButton": "Nieuw",
"fm_newButtonTitle": "Make een nieuwe werkomgeving of map, importeer een bestand in de huidige map",
"fm_newFolder": "Nieuwe map",
"fm_newFile": "Nieuwe werkomgeving",
"fm_folder": "Map",
"fm_sharedFolder": "Gedeelde map",
"fm_folderName": "Mapnaam",
"fm_numberOfFolders": "Aantal mappen",
"fm_numberOfFiles": "Aantal bestanden",
"fm_fileName": "Bestandsnaam",
"fm_title": "Titel",
"fm_type": "Type",
"fm_forbidden": "Verboden actie",
"fm_originalPath": "Oorspronkelijke pad",
"fm_openParent": "Weergeven in map",
"fm_noname": "Document Zonder Title",
"fm_emptyTrashDialog": "Weet u zeker dat u de prullenbak wilt legen?",
"fm_removeSeveralPermanentlyDialog": "Weet u zeker dat u deze {0} elementen voorgoed wilt verwijderen van uw CryptDrive?",
"fm_removePermanentlyDialog": "Weet u zeker dat u dit element voorgoed wilt verwijderen van uw CryptDrive?",
"fm_removePermanentlyNote": "Werkomgevingen in uw eigendom worden verwijderd van de server als u doorgaat.",
"fm_removeSeveralDialog": "Weet u zeker dat u deze {0} elementen wilt verplaatsen naar de prullenbak?",
"fm_removeDialog": "Weet u zeker dat u {0} wilt verplaatsen naar de prullenbak?",
"fm_deleteOwnedPad": "Weet u zeker dat u deze werkomgeving wilt verwijderen van de server?",
"fm_deleteOwnedPads": "Weet u zeker dat u deze werkomgevingen wilt verwijderen van de server?",
"fm_restoreDialog": "Weet u zeker dat u {0} wilt terugzetten naar de vorige lokatie?",
"fm_unknownFolderError": "De geselecteerde of laatst bezochte map bestaat niet meer. De bovenliggende map aan het openen...",
"fm_backup_title": "Backup link",
"fm_nameFile": "Hoe wilt u dit bestand noemen?",
"fm_error_cantPin": "Interne server fout. Graag de pagina herladen en opnieuw proberen.",
"fm_canBeShared": "Deze map is deelbaar",
"fm_prop_tagsList": "Markeringen",
"fm_burnThisDriveButton": "Verwijder alle informatie opgeslagen door CryptPad in uw browser",
"fm_padIsOwned": "U bent de eigenaar van deze werkomgeving",
"fm_padIsOwnedOther": "Deze werkomgeving is van een andere gebruiker",
"fm_deletedPads": "Deze werkomgeving bestaan niet meer op de server, ze zijn verwijderd van uw CryptDrive: {0}",
"fm_tags_name": "Markeringsnaam",
"fm_tags_used": "Aantal gebruikers",
"fm_passwordProtected": "Vergrendeld met wachtwoord",
"fc_newfolder": "Nieuwe map",
"fc_newsharedfolder": "Nieuwe gedeelde map",
"fc_rename": "Hernoemen",
"fc_color": "Kleur wijzigen",
"fc_open": "Openen",
"fc_openInCode": "Openen in Code bewerker",
"fc_expandAll": "Alles Uitvouwen",
"fc_collapseAll": "Alles Samenvouwen",
"fc_delete": "Naar prullenbak verplaatsen",
"fc_delete_owned": "Verwijderen van server",
"fc_restore": "Herstellen",
"fc_remove": "Verwijder uit uw CryptDrive",
"fc_remove_sharedfolder": "Verwijderen",
"fc_empty": "Prullenbak legen",
"fc_prop": "Eigenschappen",
"fc_hashtag": "Markeringen",
"getEmbedCode": "Krijg integratiecode",
"kanban_done": "Gedaan",
"poll_comment_remove": "Verwijder dit commentaar",
"profile_fieldSaved": "Nieuwe waarde opgeslagen: {0}",
"fm_morePads": "Meer"
}

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/onlyoffice/main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/onlyoffice/main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -731,10 +731,30 @@ define([
$iframe.find('body').attr('spellcheck', true);
}
});
Messages.pad_useFullWidth = "Use full width"; // XXX
Messages.pad_usePageWidth = "Use page mode"; // XXX
framework._.sfCommon.getAttribute(['pad', 'width'], function (err, data) {
if (data) {
var active = data || typeof(data) === "undefined";
if (active) {
$iframe.find('html').addClass('cke_body_width');
}
var $width = framework._.sfCommon.createButton('', true, {
icon: 'fa-arrows-h',
text: active ? Messages.pad_useFullWidth : Messages.pad_usePageWidth,
name: "pad-width",
},function () {
if (active) {
$iframe.find('html').removeClass('cke_body_width');
} else {
$iframe.find('html').addClass('cke_body_width');
}
active = !active;
var key = active ? Messages.pad_useFullWidth : Messages.pad_usePageWidth;
$width.find('.cp-toolbar-drawer-element').text(key);
framework._.sfCommon.setAttribute(['pad', 'width'], active);
});
framework._.toolbar.$drawer.append($width);
});
framework._.sfCommon.isPadStored(function (err, val) {

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/onlyoffice/main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<link href="/customize/src/outer.css" rel="stylesheet" type="text/css">
<link href="/customize/src/outer.css?ver=1.1" rel="stylesheet" type="text/css">
</head>
<body>
<iframe id="sbox-iframe">