Merge branch 'soon'

pull/1/head
ansuz 6 years ago
commit 5cfc77e8b5

@ -103,7 +103,7 @@ define([
])*/
])
]),
h('div.cp-version-footer', "CryptPad v2.21.0 (Vervet)")
h('div.cp-version-footer', "CryptPad v2.22.0 (Wolf)")
]);
};

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

@ -452,7 +452,7 @@ define([
}
common.getAttribute(['general', 'share'], function (err, val) {
val = val || {};
if (val.edit === false) {
if (val.edit === false || !hashes.editHash) {
$(link).find('#cp-share-editable-false').prop('checked', true);
$(link).find('#cp-share-editable-true').prop('checked', false);
} else {
@ -719,7 +719,8 @@ define([
}
}
sframeChan.query('Q_SAVE_AS_TEMPLATE', {
toSave: toSave
toSave: toSave,
title: title
}, function () {
UI.alert(Messages.templateSaved);
Feedback.send('TEMPLATE_CREATED');

@ -28,9 +28,9 @@ define([
}
};
var makeConfig = function (hash, password) {
var makeConfig = function (hash, opt) {
// We can't use cryptget with a file or a user so we can use 'pad' as hash type
var secret = Hash.getSecrets('pad', hash, password);
var secret = Hash.getSecrets('pad', hash, opt.password);
if (!secret.keys) { secret.keys = secret.key; } // support old hashses
var config = {
websocketURL: NetConfig.getWebsocketURL(),
@ -38,6 +38,7 @@ define([
validateKey: secret.keys.validateKey || undefined,
crypto: Crypto.createEncryptor(secret.keys),
logLevel: 0,
initialState: opt.initialState
};
return config;
};
@ -57,7 +58,7 @@ define([
}
opt = opt || {};
var config = makeConfig(hash, opt.password);
var config = makeConfig(hash, opt);
var Session = { cb: cb, hasNetwork: Boolean(opt.network) };
config.onReady = function (info) {
@ -82,7 +83,7 @@ define([
}
opt = opt || {};
var config = makeConfig(hash, opt.password);
var config = makeConfig(hash, opt);
var Session = { cb: cb, };
config.onReady = function (info) {

@ -477,6 +477,9 @@ define([
// PPP: password for the new template?
var hash = Hash.createRandomHash(p.type);
var href = '/' + p.type + '/#' + hash;
var optsPut = {};
if (p.type === 'poll') { optsPut.initialState = '{}'; }
// PPP: add password as cryptput option
Cryptput(hash, data.toSave, function (e) {
if (e) { throw new Error(e); }
@ -488,7 +491,7 @@ define([
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
});
}, optsPut);
};
common.isTemplate = function (href, cb) {
@ -512,6 +515,10 @@ define([
optsPut = optsPut || {};
var optsGet = {};
if (parsed.type === 'poll') { optsGet.initialState = '{}'; }
if (parsed2.type === 'poll') { optsPut.initialState = '{}'; }
Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
@ -656,6 +663,12 @@ define([
};
cursor.onEvent = Util.mkEvent();
// Mailbox
var mailbox = common.mailbox = {};
mailbox.execCommand = function (data, cb) {
postMessage("MAILBOX_COMMAND", data, cb);
};
mailbox.onEvent = Util.mkEvent();
// Pad RPC
var pad = common.padRpc = {};
@ -1096,6 +1109,8 @@ define([
CHAT_EVENT: common.messenger.onEvent.fire,
// Cursor
CURSOR_EVENT: common.cursor.onEvent.fire,
// Mailbox
MAILBOX_EVENT: common.mailbox.onEvent.fire,
// Pad
PAD_READY: common.padRpc.onReadyEvent.fire,
PAD_MESSAGE: common.padRpc.onMessageEvent.fire,

@ -12,18 +12,19 @@ define([
'/common/common-messenger.js',
'/common/outer/cursor.js',
'/common/outer/onlyoffice.js',
'/common/outer/chainpad-netflux-worker.js',
'/common/outer/mailbox.js',
'/common/outer/network-config.js',
'/customize/application_config.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/nthen/index.js',
'/bower_components/saferphore/index.js',
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
Cursor, OnlyOffice, CpNfWorker, NetConfig, AppConfig,
Crypto, ChainPad, Listmap, nThen, Saferphore) {
Cursor, OnlyOffice, Mailbox, NetConfig, AppConfig,
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
var create = function () {
var Store = window.Cryptpad_Store = {};
@ -478,10 +479,11 @@ define([
Store.addPad = function (clientId, data, cb) {
if (!data.href && !data.roHref) { return void cb({error:'NO_HREF'}); }
var secret;
if (!data.roHref) {
var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData.type === "pad") {
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
data.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
}
}
@ -489,7 +491,7 @@ define([
if (data.owners) { pad.owners = data.owners; }
if (data.expire) { pad.expire = data.expire; }
if (data.password) { pad.password = data.password; }
if (data.channel) { pad.channel = data.channel; }
if (data.channel || secret) { pad.channel = data.channel || secret.channel; }
store.manager.addPad(data.path, pad, function (e) {
if (e) { return void cb({error: e}); }
sendDriveEvent('DRIVE_CHANGE', {
@ -939,7 +941,6 @@ define([
};
// Cursor
Store.cursor = {
execCommand: function (clientId, data, cb) {
if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); }
@ -947,6 +948,14 @@ define([
}
};
// Mailbox
Store.mailbox = {
execCommand: function (clientId, data, cb) {
if (!store.mailbox) { return void cb ({error: 'Mailbox is disabled'}); }
store.mailbox.execCommand(clientId, data, cb);
}
};
// Admin
Store.adminRpc = function (clientId, data, cb) {
store.rpc.adminRpc(data, function (err, res) {
@ -1010,7 +1019,7 @@ define([
});
channel.history.forEach(function (msg) {
postMessage(clientId, "PAD_MESSAGE", {
msg: CpNfWorker.removeCp(msg),
msg: CpNetflux.removeCp(msg),
user: channel.wc.myID,
validateKey: channel.data.validateKey
});
@ -1020,14 +1029,15 @@ define([
return;
}
var conf = {
onReady: function (padData) {
channel.data = padData || {};
onReady: function (pad) {
var padData = pad.metadata || {};
channel.data = padData;
if (padData && padData.validateKey && store.messenger) {
store.messenger.storeValidateKey(data.channel, padData.validateKey);
}
postMessage(clientId, "PAD_READY");
},
onMessage: function (user, m, validateKey, isCp) {
onMessage: function (m, user, validateKey, isCp) {
channel.pushHistory(m, isCp);
channel.bcast("PAD_MESSAGE", {
user: user,
@ -1041,13 +1051,25 @@ define([
onLeave: function (m) {
channel.bcast("PAD_LEAVE", m);
},
onDisconnect: function () {
onAbort: function () {
channel.bcast("PAD_DISCONNECT");
},
onError: function (err) {
channel.bcast("PAD_ERROR", err);
delete channels[data.channel]; // TODO test?
delete channels[data.channel];
},
onChannelError: function (err) {
channel.bcast("PAD_ERROR", err);
delete channels[data.channel];
},
onConnectionChange: function () {},
crypto: {
// The encryption and decryption is done in the outer window.
// This async-store only deals with already encrypted messages.
encrypt: function (m) { return m; },
decrypt: function (m) { return m; }
},
noChainPad: true,
channel: data.channel,
validateKey: data.validateKey,
owners: data.owners,
@ -1060,10 +1082,10 @@ define([
// Send to server
sendMessage(msg, function () {
// Broadcast to other tabs
channel.pushHistory(CpNfWorker.removeCp(msg), /^cp\|/.test(msg));
channel.pushHistory(CpNetflux.removeCp(msg), /^cp\|/.test(msg));
channel.bcast("PAD_MESSAGE", {
user: wc.myID,
msg: CpNfWorker.removeCp(msg),
msg: CpNetflux.removeCp(msg),
validateKey: channel.data.validateKey
}, cId);
cb();
@ -1073,6 +1095,7 @@ define([
channel.queue.forEach(function (data) {
channel.sendMessage(data.message, clientId);
});
channel.queue = [];
channel.bcast("PAD_CONNECT", {
myID: wc.myID,
id: wc.id,
@ -1080,7 +1103,7 @@ define([
});
}
};
channel.cpNf = CpNfWorker.start(conf);
channel.cpNf = CpNetflux.start(conf);
};
Store.leavePad = function (clientId, data, cb) {
var channel = channels[data.channel];
@ -1291,6 +1314,7 @@ define([
if (messengerIdx !== -1) {
messengerEventClients.splice(messengerIdx, 1);
}
// TODO mailbox events
try {
store.cursor.removeClient(clientId);
} catch (e) { console.error(e); }
@ -1392,6 +1416,17 @@ define([
});
};
var loadMailbox = function (waitFor) {
store.mailbox = Mailbox.init(store, waitFor, function (ev, data, clients) {
clients.forEach(function (cId) {
postMessage(cId, 'MAILBOX_EVENT', {
ev: ev,
data: data
});
});
});
};
//////////////////////////////////////////////////////////////////
/////////////////////// Init /////////////////////////////////////
//////////////////////////////////////////////////////////////////
@ -1486,6 +1521,7 @@ define([
loadMessenger();
loadCursor();
loadOnlyOffice();
loadMailbox(waitFor);
}).nThen(function () {
var requestLogin = function () {
broadcast([], "REQUEST_LOGIN");

@ -1,294 +0,0 @@
/*
* Copyright 2014 XWiki SAS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define([], function () {
var USE_HISTORY = true;
var verbose = function (x) { console.log(x); };
verbose = function () {}; // comment out to enable verbose logging
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
var removeCp = function (str) {
return str.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, '');
};
var start = function (conf) {
var channel = conf.channel;
var validateKey = conf.validateKey;
var readOnly = conf.readOnly || false;
var network = conf.network;
var onConnect = conf.onConnect || function () { };
var onMessage = conf.onMessage;
var onJoin = conf.onJoin;
var onLeave = conf.onLeave;
var onReady = conf.onReady;
var onDisconnect = conf.onDisconnect;
var onError = conf.onError;
var owners = conf.owners;
var password = conf.password;
var expire = conf.expire;
var padData;
conf = undefined;
var initializing = true;
var stopped = false;
var lastKnownHash;
var messageFromOuter = function () {};
var error = function (err, wc) {
if (onError) {
onError({
type: err,
loaded: !initializing
});
if (wc && (err === "EEXPIRED" || err === "EDELETED")) { wc.leave(); }
}
else { console.error(err); }
};
var onRdy = function (padData) {
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
// message through "network" when it is synced, and it triggers onReady for each channel joined.
if (!initializing) { return; }
onReady(padData);
//sframeChan.event('EV_RT_READY', null);
// we're fully synced
initializing = false;
};
// shim between chainpad and netflux
var msgIn = function (peerId, msg) {
// NOTE: Hash version 0 contains a 32 characters nonce followed by a pipe
// at the beginning of each message on the server.
// We have to make sure our regex ignores this nonce using {0,20} (our IDs
// should only be 8 characters long)
return removeCp(msg);
};
var msgOut = function (msg) {
if (readOnly) { return; }
return msg;
};
var onMsg = function(peer, msg, wc, network, direct) {
// unpack the history keeper from the webchannel
var hk = network.historyKeeper;
if (direct && peer !== hk) {
return;
}
if (direct) {
var parsed = JSON.parse(msg);
if (parsed.validateKey && parsed.channel) {
if (parsed.channel === wc.id && !validateKey) {
validateKey = parsed.validateKey;
}
if (parsed.channel === wc.id) {
padData = parsed;
}
// We have to return even if it is not the current channel:
// we don't want to continue with other channels messages here
return;
}
if (parsed.state && parsed.state === 1 && parsed.channel) {
if (parsed.channel === wc.id) {
onRdy(padData);
}
// We have to return even if it is not the current channel:
// we don't want to continue with other channels messages here
return;
}
}
if (peer === hk) {
// if the peer is the 'history keeper', extract their message
var parsed1 = JSON.parse(msg);
// First check if it is an error message (EXPIRED/DELETED)
if (parsed1.channel === wc.id && parsed1.error) {
return void error(parsed1.error, wc);
}
msg = parsed1[4];
// Check that this is a message for our channel
if (parsed1[3] !== wc.id) { return; }
}
lastKnownHash = msg.slice(0,64);
var isCp = /^cp\|/.test(msg);
var message = msgIn(peer, msg);
verbose(message);
// slice off the bencoded header
// Why are we getting bencoded stuff to begin with?
// FIXME this shouldn't be necessary
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad
onMessage(peer, message, validateKey, isCp);
//sframeChan.query('Q_RT_MESSAGE', message, function () { });
};
// We use an object to store the webchannel so that we don't have to push new handlers to chainpad
// and remove the old ones when reconnecting and keeping the same 'realtime' object
// See realtime.onMessage below: we call wc.bcast(...) but wc may change
var wcObject = {};
var onOpen = function(wc, network, firstConnection) {
wcObject.wc = wc;
channel = wc.id;
// Add the existing peers in the userList
//TODO sframeChan.event('EV_RT_CONNECT', { myID: wc.myID, members: wc.members, readOnly: readOnly });
// Add the handlers to the WebChannel
wc.on('message', function (msg, sender) { //Channel msg
onMsg(sender, msg, wc, network);
});
wc.on('join', function (m) { onJoin(m); /*sframeChan.event('EV_RT_JOIN', m);*/ });
wc.on('leave', function (m) { onLeave(m); /*sframeChan.event('EV_RT_LEAVE', m);*/ });
if (firstConnection) {
// Sending a message...
messageFromOuter = function(message, cb) {
// Filter messages sent by Chainpad to make it compatible with Netflux
message = msgOut(message);
if (message) {
// Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we
// want to keep the same chainpad (realtime) object
try {
wcObject.wc.bcast(message).then(function() {
cb();
}, function(err) {
// The message has not been sent, display the error.
console.error(err);
});
} catch (e) {
console.log(e);
// Just skip calling back and it will fail on the inside.
}
}
};
}
onConnect(wc, messageFromOuter);
// Get the channel history
if (USE_HISTORY) {
var hk;
wc.members.forEach(function (p) {
if (p.length === 16) { hk = p; }
});
network.historyKeeper = hk;
var cfg = {
validateKey: validateKey,
lastKnownHash: lastKnownHash,
owners: owners,
expire: expire,
password: password
};
var msg = ['GET_HISTORY', wc.id, cfg];
// Add the validateKey if we are the channel creator and we have a validateKey
if (hk) {
network.sendto(hk, JSON.stringify(msg)).then(function () {
}, function (err) {
console.error(err);
});
}
} else {
onRdy();
}
};
/*var isIntentionallyLeaving = false;
window.addEventListener("beforeunload", function () {
isIntentionallyLeaving = true;
});*/
var findChannelById = function (webChannels, channelId) {
var webChannel;
// Array.some terminates once a truthy value is returned
// best case is faster than forEach, though webchannel arrays seem
// to consistently have a length of 1
webChannels.some(function(chan) {
if(chan.id === channelId) { webChannel = chan; return true;}
});
return webChannel;
};
var connectTo = function (network, firstConnection) {
// join the netflux network, promise to handle opening of the channel
network.join(channel || null).then(function(wc) {
onOpen(wc, network, firstConnection);
}, function(err) {
console.error(err);
if (onError) {
onError({
type: err && (err.type || err),
loaded: !initializing
});
}
});
};
network.on('disconnect', function (reason) {
//if (isIntentionallyLeaving) { return; }
if (reason === "network.disconnect() called") { return; }
onDisconnect();
//sframeChan.event('EV_RT_DISCONNECT');
});
network.on('reconnect', function () {
if (stopped) { return; }
initializing = true;
connectTo(network, false);
});
network.on('message', function (msg, sender) { // Direct message
if (stopped) { return; }
var wchan = findChannelById(network.webChannels, channel);
if (wchan) {
onMsg(sender, msg, wchan, network, true);
}
});
connectTo(network, true);
return {
stop: function () {
var wchan = findChannelById(network.webChannels, channel);
if (wchan) { wchan.leave(''); }
stopped = true;
}
};
};
return {
start: start,
removeCp: removeCp
/*function (config) {
config.sframeChan.whenReg('EV_RT_READY', function () {
start(config);
});
}*/
};
});

@ -0,0 +1,36 @@
// jshint ignore: start
define([
'/common/common-util.js',
'/common/common-constants.js',
'/customize/messages.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-crypto/crypto.js',
], function (Util, Constants, Messages, CpNetflux, Crypto) {
var Mailbox = {};
Mailbox.init = function (store, waitFor, emit) {
var mailbox = {};
var ctx = {
store: store,
emit: emit,
};
mailbox.removeClient = function (clientId) {
// TODO
//removeClient(ctx, clientId);
};
mailbox.leavePad = function (padChan) {
// TODO
//leaveChannel(ctx, padChan);
};
mailbox.execCommand = function (clientId, obj, cb) {
var cmd = obj.cmd;
var data = obj.data;
};
return mailbox;
};
return Mailbox;
});

@ -66,6 +66,8 @@ define([
OO_COMMAND: Store.onlyoffice.execCommand,
// Cursor
CURSOR_COMMAND: Store.cursor.execCommand,
// Mailbox
MAILBOX_COMMAND: Store.mailbox.execCommand,
// Pad
SEND_PAD_MSG: Store.sendPadMsg,
JOIN_PAD: Store.joinPad,

@ -636,7 +636,7 @@ define([
}
// If we have an edit link, check the view link
if (el.href && parsed.hashData.type === "pad") {
if (el.href && parsed.hashData.type === "pad" && parsed.hashData.version) {
if (parsed.hashData.mode === "view") {
el.roHref = el.href;
delete el.href;
@ -651,6 +651,10 @@ define([
}
}
}
// v0 hashes don't support read-only
if (parsed.hashData.version === 0) {
delete el.roHref;
}
// Fix href
if (el.href && /^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }

@ -961,7 +961,20 @@ define([
};
var getRecentPads = function (Env) {
return Env.user.userObject.getRecentPads();
var files = [];
var userObjects = _getUserObjects(Env);
userObjects.forEach(function (uo) {
var data = uo.getFiles([UserObject.FILES_DATA]).map(function (id) {
return [Number(id), uo.getFileData(id)];
});
Array.prototype.push.apply(files, data);
});
var sorted = files.filter(function (a) { return a[1].atime; })
.sort(function (a,b) {
return b[1].atime - a[1].atime;
});
return sorted;
//return Env.user.userObject.getRecentPads();
};
var getOwnedPads = function (Env) {
return Env.user.userObject.getOwnedPads(Env.edPublic);

@ -1041,5 +1041,15 @@
"contact_devHint": "Für Verbesserungsvorschläge oder zum Danke-Sagen.",
"contact_bug": "Fehlerbericht",
"contact_chat": "Chat",
"contact_email": "E-Mail"
"contact_email": "E-Mail",
"timeoutError": "Ein Fehler hat deine Verbindung zum Server unterbrochen. <br>Drücke <em>Esc</em>, um die Seite neu zu laden.",
"admin_diskUsageTitle": "Speicherplatzbelegung",
"admin_diskUsageHint": "Speicherplatz, der von verschiedenen CryptPad-Ressourcen verwendet wird",
"admin_diskUsageButton": "Bericht generieren",
"settings_codeSpellcheckTitle": "Rechtschreibprüfung",
"settings_codeSpellcheckLabel": "Rechtschreibprüfung im Code-Editor aktivieren",
"drive_active1Day": "Letzte 24 Stunden",
"drive_active7Days": "Letzte 7 Tage",
"drive_active28Days": "Letzte 4 Wochen",
"drive_activeOld": "Ältere Pads"
}

@ -10,10 +10,12 @@
"kanban": "Kanban",
"drive": "CryptDrive",
"todo": "Lista de tareas",
"file": "Archivo"
"file": "Archivo",
"media": "Media",
"sheet": "Hoja (Beta)"
},
"disconnected": "Desconectado",
"synchronizing": "Sincronización",
"synchronizing": "Sincronizando",
"reconnecting": "Reconectando...",
"lag": "Retraso",
"readonly": "Sólo lectura",
@ -373,16 +375,16 @@
"profile_urlPlaceholder": "URL",
"profile_namePlaceholder": "Nombre mostrado en su perfil",
"profile_avatar": "Imagen",
"profile_upload": "Subir una imagen",
"profile_upload": "Subir una imagen de perfil",
"profile_error": "Error al crear tu perfil: {0}",
"profile_register": "Tienes que registrarte para crear un perfil",
"profile_register": "Tienes que ingresar para crear un perfil!",
"profile_create": "Crear perfil",
"profile_description": "Descripción",
"profile_fieldSaved": "Guardado: {0}",
"download_mt_button": "Descargar",
"updated_0_header_logoTitle": "Volver a tu CryptDrive",
"realtime_unrecoverableError": "El motor de tiempo real ha encontrado un error. Haga clic en OK para recargar la página.",
"typing": "Escribiendo",
"typing": "Editando",
"profile_inviteButton": "Conectar",
"profile_inviteButtonTitle": "Crear un enlace de invitación para este usuario.",
"profile_inviteExplanation": "Hacer clic en <strong>OK</strong> creará un enlace de mensaje seguro que <em>sólo {0} podrá ver.</em><br><br>El enlace será copiado a tu portapapeles y puede ser compartido públicamente.",
@ -435,5 +437,129 @@
"settings_codeUseTabs": "Utilizar tabulaciones en vez de espacios",
"pad_showToolbar": "Mostrar la barra de herramientas",
"pad_hideToolbar": "Esconder la barra de herramientas",
"main_catch_phrase": "El Cloud Zero Knowledge"
"main_catch_phrase": "El Cloud Zero Knowledge",
"button_newkanban": "Nuevo Kanban",
"button_newsheet": "Nueva Hoja",
"padNotPinned": "Esta nota expirará luego de 3 meses de inactividad, {0}ingresar{1} o {2}registrarse{3}para conservar",
"anonymousStoreDisabled": "El webmaster de esta instancia de CryptPad a deshabilitado al almacenamiento para usuarios anónimos. Debes ingresar para poder usar CrytDrive.",
"expiredError": "Este pad ha expirado y ya no está disponible",
"deletedError": "Esta nota ha sido borrada por su dueño y ya no está disponible.",
"inactiveError": "Esta nota ha sido eliminada por inactividad. Presione Esc para crear una nueva nota.",
"chainpadError": "Ha ocurrido un error crítico al actualizar su contenido. Esta página esta en modo de sólo lectura, para asegurarse que no perderá su trabajo.<br>Hit<em>Esc</em>para continuar y ver esta nota, o recargar para editar nuevamente.",
"invalidHashError": "El documento que has solicitado tiene una URL invalida.",
"errorCopy": " Aún puedes copiar el contenido a otra ubicación apretando <em>Esc</em>.<br>Una vez que dejes esta página desaparecerá para siempre!",
"errorRedirectToHome": "Presiona<em>Esc</em>para ser redirigido a tu Cryptdrive.",
"newVersionError": "Una nueva versión de CryptPad está disponible.<br><a href='#'>Recargar</a> para usar la nueva versión, o presiona escape para acceder a tu contenido en <b>modo offline</>.",
"deletedFromServer": "Nota borrada desde el servidor",
"mustLogin": "Debes haber ingresado a tu cuenta para acceder a esta página",
"disabledApp": "Esta nota ha sido eliminada por inactividad. Presione Esc para crear una nueva nota.",
"initializing": "Iniciando...",
"forgotten": "Movido a la basura",
"errorState": "Error crítico: {0}",
"userlist_offline": "Actualmente estás desconectado, la lista del usuario ya no está disponible.",
"chatButton": "Chat",
"useTemplate": "Comenzar con una plantilla?",
"useTemplateOK": "Elija una plantilla (Enter)",
"template_import": "Importar una plantilla",
"template_empty": "No hay plantillas disponibles",
"propertiesButton": "Propiedades",
"propertiesButtonTitle": "Obtener las propiedades de esta nota",
"printButtonTitle2": "Imprimir el documento o exportar como archivo PDF",
"printBackground": "Usar una imagen de fondo",
"printBackgroundButton": "Elija una imagen",
"printBackgroundValue": "<b>Fondo de pantalla actual</b><em>{0</em>}",
"printBackgroundRemove": "Eliminar este fondo de pantalla",
"tags_title": "Etiquetas (sólo para tí)",
"tags_add": "Actualizar las etiquetas de esta página",
"tags_searchHint": "Comenzar una búsqueda con # en tú CryptDrive para encontrar las notas etiquetadas",
"tags_notShared": "Tus etiquetas no están compartidas con otros usuarios",
"tags_duplicate": "Duplicar etiquetas:{0}",
"tags_noentry": "No puedes etiquetar una nota eliminada!",
"slide_invalidLess": "Estilo personalizado no válido",
"fileShare": "Copiar link",
"ok": "OK",
"doNotAskAgain": "No preguntar nuevamente (Esc)",
"show_help_button": "Mostrar ayuda",
"hide_help_button": "Esconder ayuda",
"help_button": "Ayuda",
"history_loadMore": "Cargar más historial",
"pad_mediatagWidth": "Ancho (px)",
"pad_mediatagHeight": "Altura (px)",
"pad_mediatagRatio": "Mantener proporción",
"pad_mediatagBorder": "Ancho del borde (px)",
"pad_mediatagPreview": "Previsualizar",
"pad_mediatagImport": "Grabar en su CryptDrive",
"pad_mediatagOptions": "Propiedades de la Imagen",
"kanban_newBoard": "Nueva Pizarra",
"kanban_item": "Item {0}",
"kanban_todo": "Por hacer",
"kanban_done": "Hecho",
"kanban_working": "En progreso",
"kanban_deleteBoard": "Estás seguro que quieres eliminar esta pizarra?",
"kanban_addBoard": "Agregar una pizarra",
"kanban_removeItem": "Remover este item",
"kanban_removeItemConfirm": "Estás seguro que quieres eliminar este ítem?",
"printBackgroundNoValue": "<em>No se muestra fondo de pantalla</em>",
"getEmbedCode": "Obtener el código insertado",
"viewEmbedTitle": "Insertar la nota en una página externa",
"viewEmbedTag": "Para insertar esta nota, incluya este iframe en su página donde usted quiera. Puede darle estilo usando CSS o atributos HTML.",
"fileEmbedTitle": "Insertar el archivo en una pagina externa",
"fileEmbedScript": "Para insertar este archivo, incluya este código una vez en su página para cargar el Etiqueta de Media:",
"fileEmbedTag": "Luego ponga esta Etiqueta de Media donde quiera que sea incorporada en su página: ",
"pad_mediatagTitle": "Opciones de Etiquetas de Media",
"poll_bookmark_col": "Agregue esta columna a sus marcadores así estará siempre desbloqueada y se le mostrará al comienzo",
"poll_bookmarked_col": "Esta es su columna de bookmarks. Siempre será desbloqueada y mostrada al comienzo para usted.",
"poll_total": "TOTAL",
"poll_comment_list": "Comentarios",
"poll_comment_add": "Agregue un comentario",
"poll_comment_submit": "Enviar",
"poll_comment_remove": "Elimine este comentario",
"poll_comment_placeholder": "Su comentario",
"poll_comment_disabled": "Publique esta encuesta usando el botón ✓ para habilitar los comentarios.",
"oo_reconnect": "La conexión con el servidor se ha restablecido. Haga clic en OK para recargar y continúe con la edición.",
"oo_cantUpload": "Subir no está permitido mientras usuarios están presentes.",
"oo_uploaded": "La subida de archivos se ha completado. Clic en OK para recargar la página o cancelar para continuar en modo de solo lectura.",
"canvas_imageEmbed": "Añada una imagen desde su computador",
"profile_uploadSizeError": "Error: su imagen de perfil debe ser menor que {0}",
"profile_uploadTypeError": "Error: su imagen de perfil no es permitida. Los tipos permitidos son: {0}",
"contacts_warning": "Todo lo que escribas acá es persistente y estará disponible para todo los usuarios de esta nota. Sea precavido con la información sensible e importante!",
"contacts_padTitle": "Chat",
"contacts_fetchHistory": "Recuperar la historial antigua",
"contacts_friends": "Amigos",
"contacts_rooms": "Sala de chat",
"contacts_leaveRoom": "Deja esta sala",
"contacts_online": "Otro usuario de esta sala está online",
"debug_getGraph": "Obtenga el código para generar un grafico de este documento",
"debug_getGraphWait": "Generando el gráfico... Por favor espere.",
"debug_getGraphText": "Este es el código DOT para generar un gráfico del historial de este documento:",
"fm_recentPadsName": "Notas recientes",
"fm_ownedPadsName": "Dueño",
"fm_tagsName": "Etiquetas",
"fm_sharedFolderName": "Carpeta compartida",
"fm_sharedFolder": "Carpeta compartida",
"fm_removePermanentlyNote": "Sus notas serán removidas del servidor si continua.",
"fm_deleteOwnedPad": "Está seguro que quiere remover esta nota del servidor de manera permanente?",
"fm_deleteOwnedPads": "Está seguro que quiere remover estas notas del servidor de manera permanente?",
"fm_info_recent": "Lista de notas recientemente abiertas o modificadas.",
"fm_info_sharedFolder": "Esta es una carpeta compartida. Usted no ha ingresado a su cuenta por lo que solo tiene acceso en modo solo lectura. <br><a href=\"/registrarse/\">ingresar</a> o <a href=\"/ingresar/\">ingresar</a> para poder importarla a su Cryptdrive y modificarla.",
"fm_info_owned": "Usted es el dueño de las notas que se presentan. Esto significa que puede removerlas de manera permanente del servidor cuando lo desee. Si lo hace, los otros usuarios no podrán acceder a ellas nunca más.",
"fm_renamedPad": "Ha definido un nombre personalizado para esta nota. El título que se comparte es: br><b>{0}</b>",
"fm_canBeShared": "Esta carpeta puede ser compartida",
"fm_prop_tagsList": "Etiquetas",
"fm_burnThisDriveButton": "Borre toda la información que ha sido almacenada por CryptPad en su navegador",
"fm_burnThisDrive": "Está seguro que quiere remover todo lo que ha sido almacenado por CryptPad en su navegador? <br> Esto removerá su CryptDrive y su historial de su navegador, pero sus notas aun existirán (encriptadas) en nuestro servidor.",
"fm_padIsOwned": "Usted es el dueño de esta nota",
"fm_padIsOwnedOther": "El dueño de esta nota es otro usuario",
"fm_deletedPads": "Estas notas ya no existen en este servidor, han sido removidas desde su CryptDrive: {0}",
"fm_tags_name": "Nombre de la Etiqueta",
"fm_tags_used": "Número de usos",
"fm_restoreDrive": "Su drive se está reseteando a un estado anterior. Para mejores resultados evite hacer cambios a su drive hasta que el proceso se haya completado.",
"fm_moveNestedSF": "No puedes poner una carpeta compartida dentro de otra. La carpeta {0} no se movió.",
"fm_passwordProtected": "Este documento está protegido por una contraseña",
"fc_newsharedfolder": "Nueva carpeta compartida",
"fc_delete_owned": "Eliminar desde el servidor",
"fc_remove_sharedfolder": "Eliminar",
"fc_hashtag": "Etiquetas",
"register_passwordTooShort": "La contraseña debe tener por los menos {0} caracteres de largo.",
"useTemplateCancel": "Recomenzar (Esc)"
}

@ -1047,5 +1047,9 @@
"admin_diskUsageHint": "Quantité d'espace de stockage utilisé par la base de données de CryptPad",
"admin_diskUsageButton": "Générer le rapport",
"settings_codeSpellcheckTitle": "Vérification orthographique",
"settings_codeSpellcheckLabel": "Activer la vérification orthographique"
"settings_codeSpellcheckLabel": "Activer la vérification orthographique",
"drive_active1Day": "Dernières 24 heures",
"drive_active7Days": "7 derniers jours",
"drive_active28Days": "4 dernières semaines",
"drive_activeOld": "Pads moins récents"
}

@ -358,5 +358,74 @@
"fm_info_template": "Contiene tutti i pads salvati come modelli e che puoi riutilizzare per creare nuovi pad.",
"fm_info_recent": "Lista dei pads recentemente aperti o modificati.",
"fm_info_trash": "Svuota il tuo cestino per liberare spazio nel tuo CryptDrive.",
"fm_info_allFiles": "Contiene tutti i files da \"Documenti\", \"Non catalogati\", \"Cestino\". Non puoi muovere o cancellare files da qui."
"fm_info_allFiles": "Contiene tutti i files da \"Documenti\", \"Non catalogati\", \"Cestino\". Non puoi muovere o cancellare files da qui.",
"fm_info_anonymous": "Non sei loggato, quindi i tuoi pads scadranno tra tre mesi (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">find out more</a>). Sono salvati nel tuo browser, quindi cancellando la cronologia potresti farli scomparire.<br><a href=\"/register/\">Registrati</a> o <a href=\"/login/\">Effettua il login</a> per salvarti permanentemente.<br>",
"fm_info_sharedFolder": "Questa è una cartella condivisa. Non sei loggato, quindi puoi accederla solo in modalità solo lettura.<br><a href=\"/register/\">Registrati</a> o <a href=\"/login/\">Effettua il login</a> per poterla importare nel tuo CryptDrive e per modificarla.",
"fm_info_owned": "Sei il proprietario dei pads mostrati qui. Questo significa che puoi rimuoverli permanentemente dal server, quando lo desideri. Se lo fai, gli altri utenti non potranno mai più accedervi.",
"fm_alert_backupUrl": "Link di backup per questo drive.<br>È<strong>estremamente raccomandato</strong> che tu lo tenga segreto.<br>Puoi usarlo per recuperare tutti i tuoi files nel caso in cui la memoria del tuo browser venga cancellata.<br>Chiunque in possesso di questo link può modificare o rimuovere tutti i files nel tuo file manager.<br>",
"fm_backup_title": "Link di backup",
"fm_nameFile": "Come vuoi chiamare questo file?",
"fm_error_cantPin": "Errore interno del server. Per favore, ricarica la pagina e prova di nuovo.",
"fm_viewListButton": "Visualizzazione lista",
"fm_viewGridButton": "Visualizzazione griglia",
"fm_renamedPad": "Hai impostato un nome personalizzato per questo pad. Il suo titolo condiviso è: <br><b>{0}</b>",
"fm_canBeShared": "Questa cartella può essere condivisa",
"fm_prop_tagsList": "Tags",
"fm_burnThisDriveButton": "Cancella tutte le informazioni salvate da CryptPad nel tuo browser",
"fm_burnThisDrive": "Sei sicuro di voler rimuovere tutti i dati salvati da CryptPad nel tuo browser?<br>Questo rimuoverà il tuo CryptDrive e la sua cronologia dal tuo browser, ma i tuoi pad continueranno a esistere (criptati) nel nostro server.",
"fm_padIsOwned": "Sei il proprietario di questo pad",
"fm_padIsOwnedOther": "Il proprietario di questo pad è un altro user",
"fm_deletedPads": "Questi pads non esistono più sul server, sono stati rimossi dal tuo CryptDrive: {0}",
"fm_tags_name": "Nome del tag",
"fm_tags_used": "Numero di utilizzi",
"fm_restoreDrive": "Ripristina il tuo drive ad uno stato precedente. Per i migliori risultati, evita di fare cambiamenti al tuo drive sinchè questo processo non sarà completato.",
"fm_moveNestedSF": "Non puoi spostare una cartella condivisa all'interno di un'altra. La cartella {0} non è stata spostata.",
"fm_passwordProtected": "Questo documento è protetto da una password",
"fc_newfolder": "Nuova cartella",
"fc_newsharedfolder": "Nuova cartella condivisa",
"fc_rename": "Rinomina",
"fc_open": "Apri",
"fc_open_ro": "Apri (solo lettura)",
"fc_delete": "Muovi nel cestino",
"fc_delete_owned": "Cancella dal server",
"fc_restore": "Ripristina",
"fc_remove": "Rimuovi dal tuo CryptDrive",
"fc_remove_sharedfolder": "Rimuovi",
"fc_empty": "Svuota il cestino",
"fc_prop": "Proprietà",
"fc_hashtag": "Tags",
"fc_sizeInKilobytes": "Dimensione in Kilobytes",
"fo_moveUnsortedError": "Non puoi muovere una cartella nella lista dei modelli",
"fo_existingNameError": "Il nome è già utilizzato in questa cartella. Per favore, scegline un altro.",
"fo_moveFolderToChildError": "Non puoi muovere una cartella in una contenuta in essa",
"fo_unableToRestore": "Impossibile ripristinare questo file nella sua location originaria. Puoi provare a muoverlo in una nuova posizione.",
"fo_unavailableName": "Un file o una cartella con lo stesso nome esiste già nella nuova posizione. Rinomina l'elemento e prova di nuovo.",
"fs_migration": "Il tuo CryptDrive sta per essere aggiornato a una nuova versione. La pagina corrente sarà ricaricata.<br><strong>Per favore, ricarica la pagina per continuare a usarla.</strong>",
"login_login": "Login",
"login_makeAPad": "Crea un pad in maniera anonima",
"login_nologin": "Sfoglia i pads locali",
"login_register": "Registrati",
"logoutButton": "Logout",
"settingsButton": "Impostazioni",
"login_username": "Nome utente",
"login_password": "Password",
"login_confirm": "Conferma la tua password",
"login_remember": "Ricorda l'accesso",
"login_hashing": "Effettuo l'hash della tua password, questo può richiedere del tempo.",
"login_hello": "Buongiorno {0},",
"login_helloNoName": "Ciao,",
"login_accessDrive": "Accedi al tuo drive",
"login_orNoLogin": "o",
"login_noSuchUser": "Nome utente o password non validi. Prova di nuovi, oppure registrati",
"login_invalUser": "Nome utente richiesto",
"login_invalPass": "Password richiesta",
"login_unhandledError": "Si è verificato un errore inaspettato :(",
"register_importRecent": "Importa i pads dalla tua sessione anonima",
"register_acceptTerms": "Accetto <a href='/terms.html' tabindex='-1'>i termini del servizio</a>",
"register_passwordsDontMatch": "Le passwords non corrispondono!",
"register_passwordTooShort": "Le passwords devono essere lunghe almeno {0} caratteri.",
"register_mustAcceptTerms": "Devi accettare i termini del servizio.",
"register_mustRememberPass": "Non possiamo ripristinare la tua password se la dimentichi. È estremamente importante che tu la ricordi! Per favore, spunta la checkbox per confermare.",
"register_whyRegister": "Perché registrarsi?",
"register_header": "Benvenuto su CryptPad"
}

@ -1047,5 +1047,9 @@
"admin_diskUsageHint": "Amount of storage space consumed by various CryptPad resources",
"admin_diskUsageButton": "Generate report",
"settings_codeSpellcheckTitle": "Spellcheck",
"settings_codeSpellcheckLabel": "Enable spellcheck in the code editor"
"settings_codeSpellcheckLabel": "Enable spellcheck in the code editor",
"drive_active1Day": "Last 24 hours",
"drive_active7Days": "Last 7 days",
"drive_active28Days": "Last 4 weeks",
"drive_activeOld": "Less recent pads"
}

@ -514,5 +514,45 @@
"fm_padIsOwnedOther": "Acest pad îi aparține altui utilizator",
"fm_deletedPads": "Aceste pad-uri nu mai există pe server, au fost șterse din CryptDrive: {0}",
"fm_tags_name": "Numele etichetei",
"fm_tags_used": "Numărul de utilizări"
"fm_tags_used": "Numărul de utilizări",
"fm_restoreDrive": "Drive-ul tău va fi setat la o versiune aneterioară. Pentru a obține cele mai bune rezultate, te rugăm să eviți să aduci modificări în drive-ul tău până când procesul va fi terminat.",
"fm_moveNestedSF": "Nu poti plasa un folder partajat în interiorul altuia. Folderul {0} nu a fost mutat.",
"fm_passwordProtected": "Acest document este protejat cu o parolă",
"fc_newsharedfolder": "Folder partajat nou",
"fc_delete_owned": "Șterge de pe server",
"fc_remove_sharedfolder": "Șterge",
"fc_hashtag": "Etichete",
"fs_migration": "CrytpDrive-ul tău este updatat la o versiune nouă. În consecință, pagina curentă trebuie reîncarcată. Te rugăm să reîncarci această pagina pentru a continua să o utilizezi.",
"register_passwordTooShort": "Parolele trebuie să aibă o lungime de cel putin {0} caractere.",
"register_whyRegister": "De ce să te înscrii?",
"settings_cat_account": "Cont",
"settings_cat_drive": "CrytpDrive",
"settings_cat_cursor": "Cursor",
"settings_cat_code": "Cod",
"settings_cat_pad": "Rich text",
"settings_cat_creation": "Pad nou",
"settings_cat_subscription": "Abonare",
"settings_backupCategory": "Backup",
"settings_backupHint": "Fă backup sau restaurează tot conținutul CryptDrive-ului tău. Aceasta nu va însemna conținutul pad-urilor tale, ci doar modalitatea de a le accesa",
"settings_backupHint2": "Descarcă conținutul actual al pad-urilor tale. Ele vor fi descărcate într-un format care poate fi citit, dacă un astfel de format este disponibil.",
"settings_backup2": "Descarcă-mi CryptDrive-ul",
"settings_backup2Confirm": "Această operatiune va descărca toate pad-urile și fișierele din CryptDrive-ul tău. Dacă vrei să continui, alege un nume și apasă OK",
"settings_exportTitle": "Exportă-ți CryptDrive-ul",
"settings_exportDescription": "Te rugăm să aștepți descărcarea și decriptarea documentelor tale. Această operațiune poate dura câteva minute. Închiderea ferestrei va întrerupe acest proces.",
"settings_exportFailed": "Dacă descărcarea unui pad durează mai mult de 1 minut, acesta nu va fi inclus în export. Exportul va conține link-uri către toate pad-urile care nu au fost descărcate.",
"settings_exportWarning": "Notă: acest program este în versiune beta și ar putea avea probleme de scalabilitate. Pentru o performanță mai bună, îți recomandam să rămâi pe acest tab",
"settings_exportCancel": "Ești sigur că vrei să anulezi exportul? Va trebui să-l rulezi de la început data viitoare.",
"settings_export_reading": "Citire CryptDrive...",
"settings_export_download": "Descărcarea și decriptarea documentelor tale este în curs...",
"settings_export_compressing": "Comprimăm datele...",
"settings_export_done": "Descărcarea este gata!",
"settings_exportError": "Vizualizează erorile",
"settings_exportErrorDescription": "Următoarele documente nu au putut fi adăugate în export:",
"settings_exportErrorEmpty": "Acest document nu poate fi exportat (conținut gol sau invalid)",
"settings_exportErrorMissing": "Acest document lipsește de pe serverele noastre (este expirat sau a fost șters de către proprietarul său)",
"settings_exportErrorOther": "A apărut o eroare in timpul exportului acestui document: {0}",
"settings_resetNewTitle": "Curățare CryptDrive",
"settings_resetButton": "Șterge",
"settings_resetTipsAction": "Resetează",
"settings_thumbnails": "Miniaturi"
}

@ -335,5 +335,22 @@
"contacts_info4": "Любой участник может полностью очистить историю чата",
"contacts_confirmRemoveHistory": "Вы уверены, что хотите навсегда удалить свою историю чата? Данные не могут быть восстановлены",
"contacts_removeHistoryServerError": "При удалении истории чата произошла ошибка. Попробуйте позже еще раз",
"contacts_online": "Другой пользователь из этой комнаты находится онлайн"
"contacts_online": "Другой пользователь из этой комнаты находится онлайн",
"fm_newButtonTitle": "Создайте новый пэд или папку, импортируйте файл в текущую папку",
"fm_lastAccess": "Последний доступ",
"fm_creation": "Создание",
"fm_forbidden": "Запрещенное действие",
"fm_removePermanentlyDialog": "Вы уверены, что хотите навсегда удалить этот элемент из вашего хранилища?",
"fm_removeSeveralDialog": "Вы уверены, что хотите перенести эти {0} элементы в корзину?",
"fm_removeDialog": "Вы уверены, что хотите переместить {0} в корзину?",
"fm_deleteOwnedPad": "Вы уверены, что хотите навсегда удалить этот пэд с сервера?",
"fm_deleteOwnedPads": "Вы уверены, что хотите навсегда удалить эти пэды с сервера?",
"fm_restoreDialog": "Вы уверены, что хотите восстановить {0} в прежнее местоположение?",
"fm_unknownFolderError": "Выбранный или последний раз посещенный каталог больше не существует. Открывается изначальная папка...",
"fm_contextMenuError": "Невозможно открыть контекстное меню для данного элемента. Если проблема остается, попробуйте перезагрузить страницу.",
"fm_selectError": "Невозможно выбрать выбранный элемент. Если проблема сохраняется, попробуйте перезагрузить страницу.",
"fm_info_root": "Создавайте здесь неограниченное количество вложенных папок, чтобы сортировать ваши файлы.",
"fm_info_template": "Содержит все пэды, сохраненные в виде шаблонов, которые можно использовать повторно при создании нового пэда.",
"fm_info_recent": "Показать недавно измененные или открытые пэды.",
"fm_info_trash": "Опустошите корзину, чтобы освободить место в хранилище."
}

@ -95,6 +95,8 @@ define([
exp.isReadOnlyFile = function (element) {
if (!isFile(element)) { return false; }
var data = exp.getFileData(element);
// undefined means this pad doesn't support read-only
if (!data.roHref) { return; }
return Boolean(data.roHref && !data.href);
};

@ -509,6 +509,11 @@
}
}
}
&.cp-app-drive-element-separator {
text-align: left;
font-weight: bold;
margin-top: 0;
}
}
}
.cp-app-drive-element {
@ -521,6 +526,12 @@
&.cp-app-drive-element {
position: relative;
}
&.cp-app-drive-element-separator {
display: block;
height: auto;
width: auto;
border: none !important;
}
input {
width: 100%;
margin: 0;
@ -600,6 +611,14 @@
height: @variables_bar-height;
line-height: @variables_bar-height;
}
&.cp-app-drive-element-separator {
position: relative;
height: 1.5 * @variables_bar-height;
line-height: 1.5 * @variables_bar-height;
span {
position: absolute;
}
}
&.cp-app-drive-element-header {
cursor: default;
color: @drive_table-header-fg;

@ -2454,24 +2454,55 @@ define([
var displayRecent = function ($list) {
var filesList = manager.getRecentPads();
var limit = 20;
var now = new Date();
var last1 = new Date(now);
last1.setDate(last1.getDate()-1);
var last7 = new Date(now);
last7.setDate(last7.getDate()-7);
var last28 = new Date(now);
last28.setDate(last28.getDate()-28);
var header7, header28, headerOld;
var i = 0;
filesList.forEach(function (id) {
if (i >= limit) { return; }
// Check path (pad exists and not in trash)
var channels = [];
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active1Day)));
filesList.some(function (arr) {
if (i >= limit) { return true; }
var id = arr[0];
var file = arr[1];
if (!file || !file.atime) { return; }
if (file.atime <= last28 && i >= limit) {
return true;
}
var paths = manager.findFile(id);
if (!paths.length) { return; }
var path = paths[0];
if (manager.isPathIn(path, [TRASH])) { return; }
// Display the pad
var file = manager.getFileData(id);
if (!file) {
//debug("Unsorted or template returns an element not present in filesData: ", href);
file = { title: Messages.fm_noname };
//return;
if (channels.indexOf(file.channel) !== -1) { return; }
channels.push(file.channel);
if (!header7 && file.atime < last1) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active7Days)));
header7 = true;
}
if (!header28 && file.atime < last7) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_active28Days)));
header28 = true;
}
if (!headerOld && file.atime < last28) {
$list.append(h('li.cp-app-drive-element-separator', h('span', Messages.drive_activeOld)));
headerOld = true;
}
// Display the pad
var $icon = getFileIcon(id);
var ro = manager.isReadOnlyFile(id);
// ro undefined mens it's an old hash which doesn't support read-only
// ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '';
var $element = $('<li>', {

@ -268,9 +268,21 @@ define([
assert(function (cb) {
console.log('START DRIVE utils');
var files = JSON.parse(JSON.stringify(example));
var href6 = "/pad/#67a9385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyGbj7LNy/";
var id6 = 1000000000006;
var data = {
href: href6,
title: 'Title6',
atime: +new Date(),
ctime: +new Date()
};
files.filesData[id6] = data;
var fo = FO.init(files, config);
fo.fixFiles();
if (fo.isFile({}) || fo.isFile(href1) || !fo.isFile(href1, true) || !fo.isFile(id1)) {
console.log("DRIVE utils: isFile returns an incorrect value");
return cb();
@ -283,6 +295,10 @@ define([
console.log("DRIVE utils: isReadOnlyFile returns false for a 'view' file");
return cb();
}
if (typeof fo.isReadOnlyFile(id6) !== "undefined") {
console.log("DRIVE utils: isReadOnlyFile should return undefined for a v0 hash");
return cb();
}
if (!fo.hasSubfolder(files.root.Folder) || fo.hasSubfolder(files.root.Folder2)) {
console.log("DRIVE utils: hasSubfolder returns an incorrect value");
return cb();
@ -303,7 +319,7 @@ define([
console.log("DRIVE utils: 'find' returns an incorrect value");
return cb();
}
if (fo.getFiles().length !== 4 || fo.getFiles(['trash']).length !== 2) {
if (fo.getFiles().length !== 5 || fo.getFiles(['trash']).length !== 2) {
console.log("DRIVE utils: getFiles returns an incorrect value");
return cb();
}

@ -832,9 +832,9 @@ define([
var localStore = window.cryptpadStore;
$button.click(function () {
Object.keys(localStore).forEach(function (k) {
Object.keys(localStore.store).forEach(function (k) {
if(k.slice(0, 9) === "hide-info") {
localStore.put(k, undefined);
localStore.put(k, null);
}
});
UI.alert(Messages.settings_resetTipsDone);

Loading…
Cancel
Save