manual merge

pull/1/head
Caleb James DeLisle 7 years ago
commit e78cc4ce7e

1
.gitignore vendored

@ -1,4 +1,5 @@
datastore datastore
tasks
www/bower_components/* www/bower_components/*
node_modules node_modules
/config.js /config.js

@ -194,6 +194,18 @@ module.exports = {
*/ */
}, },
/* some features may require that the server be able to schedule tasks
far into the future, such as:
> "three months from now, this channel should expire"
To disable these features, set 'enableTaskScheduling' to false
*/
enableTaskScheduling: true,
/* if you would like the list of scheduled tasks to be stored in
a custom location, change the path below:
*/
taskPath: './tasks',
/* /*
* By default, CryptPad also contacts our accounts server once a day to check for changes in * By default, CryptPad also contacts our accounts server once a day to check for changes in
* the people who have accounts. This check-in will also send the version of your CryptPad * the people who have accounts. This check-in will also send the version of your CryptPad

@ -79,7 +79,7 @@ define(req, function(Util, Default, Language) {
}); });
Object.keys(translation).forEach(function (k) { Object.keys(translation).forEach(function (k) {
if (/^_/.test(k) || k === 'driveReadme') { return; } if (/^_/.test(k) || k === 'driveReadme') { return; }
if (!Default[k]) { if (typeof Default[k] === "undefined") {
missing.push([code, k, 0]); missing.push([code, k, 0]);
} }
}); });

@ -100,6 +100,7 @@ define([
h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [ h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [
h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.topbar_whatIsCryptpad), h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.topbar_whatIsCryptpad),
h('a.nav-item.nav-link', { href: 'https://blog.cryptpad.fr/'}, Msg.blog), h('a.nav-item.nav-link', { href: 'https://blog.cryptpad.fr/'}, Msg.blog),
h('a.nav-item.nav-link', { href: '/features.html'}, Msg.features),
h('a.nav-item.nav-link', { href: '/contact.html'}, Msg.contact), h('a.nav-item.nav-link', { href: '/contact.html'}, Msg.contact),
h('a.nav-item.nav-link', { href: '/about.html'}, Msg.about), h('a.nav-item.nav-link', { href: '/about.html'}, Msg.about),
].concat(rightLinks)) ].concat(rightLinks))
@ -332,6 +333,11 @@ define([
h('td') h('td')
]), ]),
]) ])
]),
h('div#cp-features-register', [
h('a', {
href: '/register/'
}, h('button.cp-features-register-button', 'Register for free'))
]) ])
]), ]),
infopageFooter() infopageFooter()
@ -601,6 +607,9 @@ define([
setHTML(h('p.register-explanation'), Msg.register_explanation) setHTML(h('p.register-explanation'), Msg.register_explanation)
]), ]),
h('div#userForm.form-group.hidden.col-md-6', [ h('div#userForm.form-group.hidden.col-md-6', [
h('a', {
href: '/features.html'
}, Msg.register_whyRegister),
h('input.form-control#username', { h('input.form-control#username', {
type: 'text', type: 'text',
autocomplete: 'off', autocomplete: 'off',

@ -139,5 +139,14 @@
} }
} }
} }
.cp-creation-settings {
justify-content: left;
a {
color: #0275d8;
&:hover {
color: lighten(#0275d8, 10%);
}
}
}
} }
} }

@ -8,9 +8,10 @@
width: 100%; width: 100%;
margin-top: 20px; margin-top: 20px;
.cp-limit-bar { .cp-limit-bar {
padding: 5px; display: inline-flex;
justify-content: center;
align-items: center;
display: inline-block;
max-width: 100%; max-width: 100%;
margin: 3px; margin: 3px;
box-sizing: border-box; box-sizing: border-box;
@ -18,7 +19,6 @@
background: white; background: white;
position: relative; position: relative;
text-align: center; text-align: center;
vertical-align: middle;
width: ~"calc(100% - 6px)"; width: ~"calc(100% - 6px)";
height: 35px; height: 35px;
line-height: 25px; line-height: 25px;

@ -50,3 +50,18 @@ table#cp-features-table {
} }
} }
#cp-features-register {
text-align: center;
padding: 20px;
}
.cp-features-register-button {
font-size: 20px;
color: #fff;
background: @cryptpad_color_blue;
border: 2px solid @cryptpad_color_blue;
border-radius: 0;
&:hover {
transform: scale(1.05);
}
}

@ -377,7 +377,8 @@ define(function () {
out.fm_emptyTrashDialog = "Êtes-vous sûr de vouloir vider la corbeille ?"; out.fm_emptyTrashDialog = "Êtes-vous sûr de vouloir vider la corbeille ?";
out.fm_removeSeveralPermanentlyDialog = "Êtes-vous sûr de vouloir supprimer ces {0} éléments de votre CryptDrive de manière permanente ?"; out.fm_removeSeveralPermanentlyDialog = "Êtes-vous sûr de vouloir supprimer ces {0} éléments de votre CryptDrive de manière permanente ?";
out.fm_removePermanentlyDialog = "Êtes-vous sûr de vouloir supprimer cet élément de votre CryptDrive de manière permanente ?"; out.fm_removePermanentlyDialog = "Êtes-vous sûr de vouloir supprimer cet élément de votre CryptDrive de manière permanente ?";
out.fm_deleteOwnedPads = "Êtes-vous sûr de vouloir supprimer définitivement ce pad du serveur ?"; out.fm_deleteOwnedPad = "Êtes-vous sûr de vouloir supprimer définitivement ce pad du serveur ?";
out.fm_deleteOwnedPads = "Êtes-vous sûr de vouloir supprimer définitivement ces pads du serveur ?";
out.fm_restoreDialog = "Êtes-vous sûr de vouloir restaurer {0} à son emplacement précédent ?"; out.fm_restoreDialog = "Êtes-vous sûr de vouloir restaurer {0} à son emplacement précédent ?";
out.fm_removeSeveralDialog = "Êtes-vous sûr de vouloir déplacer ces {0} éléments vers la corbeille ?"; out.fm_removeSeveralDialog = "Êtes-vous sûr de vouloir déplacer ces {0} éléments vers la corbeille ?";
out.fm_removeDialog = "Êtes-vous sûr de vouloir déplacer {0} vers la corbeille ?"; out.fm_removeDialog = "Êtes-vous sûr de vouloir déplacer {0} vers la corbeille ?";
@ -392,7 +393,8 @@ define(function () {
out.updated_0_fm_info_trash = "Vider la corbeille permet de libérer de l'espace dans votre CryptDrive"; out.updated_0_fm_info_trash = "Vider la corbeille permet de libérer de l'espace dans votre CryptDrive";
out.fm_info_trash = out.updated_0_fm_info_trash; out.fm_info_trash = out.updated_0_fm_info_trash;
out.fm_info_allFiles = 'Contient tous les fichiers de "Documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers depuis cet endroit.'; // Same here out.fm_info_allFiles = 'Contient tous les fichiers de "Documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers depuis cet endroit.'; // Same here
out.fm_info_anonymous = 'Vous n\'êtes pas connecté, ces pads risquent donc d\'être supprimés (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">découvrez pourquoi</a>). ' + out.fm_info_anonymous = 'Vous n\'êtes pas connecté, ces pads seront donc supprimés après 3 mois d\'inactivité (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">découvrez pourquoi</a>). ' +
'Ils sont stockés dans votre navigateur donc nettoyer votre historique peut les faire disparaître.<br>' +
'<a href="/register/">Inscrivez-vous</a> ou <a href="/login/">connectez-vous</a> pour les maintenir en vie.'; '<a href="/register/">Inscrivez-vous</a> ou <a href="/login/">connectez-vous</a> pour les maintenir en vie.';
out.fm_info_owned = "Vous êtes propriétaire des pads affichés dans cette catégorie. Cela signifie que vous pouvez choisir de les supprimer définitivement du serveur à n'importe quel moment. Ils seront alors inaccessibles pour tous les autres utilisateurs."; out.fm_info_owned = "Vous êtes propriétaire des pads affichés dans cette catégorie. Cela signifie que vous pouvez choisir de les supprimer définitivement du serveur à n'importe quel moment. Ils seront alors inaccessibles pour tous les autres utilisateurs.";
out.fm_alert_backupUrl = "Lien de secours pour ce CryptDrive.<br>" + out.fm_alert_backupUrl = "Lien de secours pour ce CryptDrive.<br>" +
@ -415,6 +417,7 @@ define(function () {
"Cette action supprimera votre CryptDrive et son historique de votre navigateur, mais les pads existeront toujours (de manière chiffrée) sur notre serveur."; "Cette action supprimera votre CryptDrive et son historique de votre navigateur, mais les pads existeront toujours (de manière chiffrée) sur notre serveur.";
out.fm_padIsOwned = "Vous êtes le propriétaire de ce pad"; out.fm_padIsOwned = "Vous êtes le propriétaire de ce pad";
out.fm_padIsOwnedOther = "Ce pad est la propriété d'un autre utilisateur"; out.fm_padIsOwnedOther = "Ce pad est la propriété d'un autre utilisateur";
out.fm_deletedPads = "Ces pads n'existent plus sur le serveur, ils ont été supprimés de votre CryptDrive: {0}";
// File - Context menu // File - Context menu
out.fc_newfolder = "Nouveau dossier"; out.fc_newfolder = "Nouveau dossier";
out.fc_rename = "Renommer"; out.fc_rename = "Renommer";
@ -474,6 +477,7 @@ define(function () {
out.register_warning = "Zero Knowledge signifie que nous ne pouvons pas récupérer vos données si vous perdez vos identifiants."; out.register_warning = "Zero Knowledge signifie que nous ne pouvons pas récupérer vos données si vous perdez vos identifiants.";
out.register_alreadyRegistered = "Cet utilisateur existe déjà, souhaitez-vous vous connecter ?"; out.register_alreadyRegistered = "Cet utilisateur existe déjà, souhaitez-vous vous connecter ?";
out.register_whyRegister = "Pourquoi s'inscrire ?";
out.register_header = "Bienvenue dans CryptPad"; out.register_header = "Bienvenue dans CryptPad";
out.register_explanation = [ out.register_explanation = [
"<p>Faisons d'abord le point sur certaines choses</p>", "<p>Faisons d'abord le point sur certaines choses</p>",
@ -567,7 +571,9 @@ define(function () {
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?"; out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?";
out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive."; out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.";
out.upload_notEnoughSpace = "Il n'y a pas assez d'espace libre dans votre CryptDrive pour ce fichier."; out.upload_notEnoughSpace = "Il n'y a pas assez d'espace libre dans votre CryptDrive pour ce fichier.";
out.upload_notEnoughSpaceBrief = "Pas assez d'espace";
out.upload_tooLarge = "Ce fichier dépasse la taille maximale autorisée."; out.upload_tooLarge = "Ce fichier dépasse la taille maximale autorisée.";
out.upload_tooLargeBrief = 'Fichier trop volumineux';
out.upload_choose = "Choisir un fichier"; out.upload_choose = "Choisir un fichier";
out.upload_pending = "En attente"; out.upload_pending = "En attente";
out.upload_cancelled = "Annulé"; out.upload_cancelled = "Annulé";
@ -577,6 +583,7 @@ define(function () {
out.upload_mustLogin = "Vous devez vous connecter pour importer un fichier"; out.upload_mustLogin = "Vous devez vous connecter pour importer un fichier";
out.download_button = "Déchiffrer et télécharger"; out.download_button = "Déchiffrer et télécharger";
out.download_mt_button = "Télécharger"; out.download_mt_button = "Télécharger";
out.download_resourceNotAvailable = "Le fichier demandé n'est pas disponible...";
out.todo_title = "CryptTodo"; out.todo_title = "CryptTodo";
out.todo_newTodoNamePlaceholder = "Décrivez votre tâche..."; out.todo_newTodoNamePlaceholder = "Décrivez votre tâche...";
@ -707,6 +714,7 @@ define(function () {
// features.html // features.html
out.features = "Fonctionnalités";
out.features_title = "Tableau des fonctionnalités"; out.features_title = "Tableau des fonctionnalités";
out.features_feature = "Fonctionnalité"; out.features_feature = "Fonctionnalité";
out.features_anon = "Utilisateur anonyme"; out.features_anon = "Utilisateur anonyme";
@ -839,6 +847,24 @@ define(function () {
out.feedback_optout = "Si vous le souhaitez, vous pouvez désactiver ces requêtes en vous rendant dans <a href='/settings/'>votre page de préférences</a>, où vous trouverez une case à cocher pour désactiver le retour d'expérience."; out.feedback_optout = "Si vous le souhaitez, vous pouvez désactiver ces requêtes en vous rendant dans <a href='/settings/'>votre page de préférences</a>, où vous trouverez une case à cocher pour désactiver le retour d'expérience.";
// Creation page // Creation page
out.creation_404 = "Le pad auquel vous souhaitez accéder n'existe plus. Vous pouvez créer un nouveau pad en utilisant le formulaire suivant.";
out.creation_ownedTitle = "Type de pad";
out.creation_ownedTrue = "Pad possédé";
out.creation_ownedFalse = "Pad ouvert";
out.creation_owned1 = "Un pad <b>possédé</b> est un pad que vous pouvez supprimer du serveur à n'importe quel moment depuis votre CryptDrive. Une fois supprimé, personne d'autre ne peut y accéder, même si le pad est stocké dans un autre CryptDrive.";
out.creation_owned2 = "Un pad <b>ouvert</b> n'a pas de propriétaire et ne peut donc pas être supprimé du serveur par un utilisateur. Il pourra tout de même être supprimé automatiquement si sa date d'expiration est dépassée.";
out.creation_expireTitle = "Durée de vie";
out.creation_expireTrue = "Ajouter durée de vie";
out.creation_expireFalse = "Illimitée";
out.creation_expireHours = "Heures";
out.creation_expireDays = "Jours";
out.creation_expireMonths = "Mois";
out.creation_expire1 = "Par défault, un pad stocké dans le CryptDrive d'un utilisateur enregistré ne sera jamais supprimé du serveur, même s'il est inactif (à moins qu'il possède un propriétaire souhaitement le supprimer).";
out.creation_expire2 = "Si vous le souhaitez, vous pouvez ajouter une durée de vie au pad afin d'être sûr qu'il soit supprimé du serveur, de manière permanente, à la date voulue.";
out.creation_createTitle = "Créer un pad";
out.creation_createFromTemplate = "Depuis un modèle";
out.creation_createFromScratch = "Nouveau pad vide";
out.creation_settings = "Préférences des nouveaux pads";
// Properties about creation data // Properties about creation data
out.creation_owners = "Propriétaires"; out.creation_owners = "Propriétaires";
out.creation_ownedByOther = "Possédé par un autre utilisateur"; out.creation_ownedByOther = "Possédé par un autre utilisateur";

@ -381,7 +381,8 @@ define(function () {
out.fm_removePermanentlyDialog = "Are you sure you want to remove that element from your CryptDrive permanently?"; out.fm_removePermanentlyDialog = "Are you sure you want to remove that element from your CryptDrive permanently?";
out.fm_removeSeveralDialog = "Are you sure you want to move these {0} elements to the trash?"; out.fm_removeSeveralDialog = "Are you sure you want to move these {0} elements to the trash?";
out.fm_removeDialog = "Are you sure you want to move {0} to the trash?"; out.fm_removeDialog = "Are you sure you want to move {0} to the trash?";
out.fm_deleteOwnedPads = "Are you sure you want to remove permanently this pad from the server?"; out.fm_deleteOwnedPad = "Are you sure you want to remove permanently this pad from the server?";
out.fm_deleteOwnedPads = "Are you sure you want to remove permanently these pads from the server?";
out.fm_restoreDialog = "Are you sure you want to restore {0} to its previous location?"; out.fm_restoreDialog = "Are you sure you want to restore {0} to its previous location?";
out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder..."; out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder...";
out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page."; out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page.";
@ -395,7 +396,8 @@ define(function () {
out.fm_info_trash = out.updated_0_fm_info_trash; out.fm_info_trash = out.updated_0_fm_info_trash;
out.fm_info_allFiles = 'Contains all the files from "Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here out.fm_info_allFiles = 'Contains all the files from "Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here
out.fm_info_anonymous = 'You are not logged in so your pads will expire after 3 months (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">find out more</a>). ' + out.fm_info_anonymous = 'You are not logged in so your pads will expire after 3 months (<a href="https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/" target="_blank">find out more</a>). ' +
'<a href="/register/">Sign up</a> or <a href="/login/">Log in</a> to keep them alive.'; 'They are stored in your browser so clearing history may make them disappear.<br>' +
'<a href="/register/">Sign up</a> or <a href="/login/">Log in</a> to keep them alive.<br>';
out.fm_info_owned = "You are the owner of the pads displayed here. This means you can remove them permanently from the server whenever you want. If you do so, other users won't be able to access them anymore."; out.fm_info_owned = "You are the owner of the pads displayed here. This means you can remove them permanently from the server whenever you want. If you do so, other users won't be able to access them anymore.";
out.fm_alert_backupUrl = "Backup link for this drive.<br>" + out.fm_alert_backupUrl = "Backup link for this drive.<br>" +
"It is <strong>highly recommended</strong> that you keep it secret.<br>" + "It is <strong>highly recommended</strong> that you keep it secret.<br>" +
@ -417,6 +419,7 @@ define(function () {
"This will remove your CryptDrive and its history from your browser, but your pads will still exist (encrypted) on our server."; "This will remove your CryptDrive and its history from your browser, but your pads will still exist (encrypted) on our server.";
out.fm_padIsOwned = "You are the owner of this pad"; out.fm_padIsOwned = "You are the owner of this pad";
out.fm_padIsOwnedOther = "This pad is owned by another user"; out.fm_padIsOwnedOther = "This pad is owned by another user";
out.fm_deletedPads = "These pads no longer exist on the server, they've been removed from your CryptDrive: {0}";
// File - Context menu // File - Context menu
out.fc_newfolder = "New folder"; out.fc_newfolder = "New folder";
out.fc_rename = "Rename"; out.fc_rename = "Rename";
@ -472,6 +475,7 @@ define(function () {
out.register_mustAcceptTerms = "You must accept the terms of service."; out.register_mustAcceptTerms = "You must accept the terms of service.";
out.register_mustRememberPass = "We cannot reset your password if you forget it. It's very important that you remember it! Please check the checkbox to confirm."; out.register_mustRememberPass = "We cannot reset your password if you forget it. It's very important that you remember it! Please check the checkbox to confirm.";
out.register_whyRegister = "Why signing up?";
out.register_header = "Welcome to CryptPad"; out.register_header = "Welcome to CryptPad";
out.register_explanation = [ out.register_explanation = [
"<h3>Lets go over a couple things first:</h3>", "<h3>Lets go over a couple things first:</h3>",
@ -717,6 +721,7 @@ define(function () {
// features.html // features.html
out.features = "Features";
out.features_title = "Features table"; out.features_title = "Features table";
out.features_feature = "Feature"; out.features_feature = "Feature";
out.features_anon = "Anonymous user"; out.features_anon = "Anonymous user";
@ -738,6 +743,7 @@ define(function () {
out.features_f_multiple = "Use on multiple devices"; out.features_f_multiple = "Use on multiple devices";
out.features_f_multiple_notes = "Easy way to access your pads from any device"; out.features_f_multiple_notes = "Easy way to access your pads from any device";
out.features_f_logoutEverywhere = "Log out from other devices"; out.features_f_logoutEverywhere = "Log out from other devices";
out.features_f_logoutEverywhere_notes = ""; // Used in the French translation to explain
out.features_f_templates = "Use templates"; out.features_f_templates = "Use templates";
out.features_f_templates_notes = "Create templates and create new pads from your templates"; out.features_f_templates_notes = "Create templates and create new pads from your templates";
out.features_f_profile = "Create a profile"; out.features_f_profile = "Create a profile";
@ -873,6 +879,7 @@ define(function () {
out.creation_createTitle = "Create a pad"; out.creation_createTitle = "Create a pad";
out.creation_createFromTemplate = "From template"; out.creation_createFromTemplate = "From template";
out.creation_createFromScratch = "From scratch"; out.creation_createFromScratch = "From scratch";
out.creation_settings = "New Pad settings";
// Properties about creation data // Properties about creation data
out.creation_owners = "Owners"; out.creation_owners = "Owners";
out.creation_ownedByOther = "Owned by another user"; out.creation_ownedByOther = "Owned by another user";

@ -11,6 +11,8 @@ var Path = require("path");
var Https = require("https"); var Https = require("https");
const Package = require('./package.json'); const Package = require('./package.json');
const Pinned = require('./pinned'); const Pinned = require('./pinned');
const Saferphore = require("saferphore");
const nThen = require("nthen");
var RPC = module.exports; var RPC = module.exports;
@ -355,6 +357,37 @@ var getMultipleFileSize = function (Env, channels, cb) {
}); });
}; };
/* accepts a list, and returns a sublist of channel or file ids which seem
to have been deleted from the server (file size 0)
we might consider that we should only say a file is gone if fs.stat returns
ENOENT, but for now it's simplest to just rely on getFileSize...
*/
var getDeletedPads = function (Env, channels, cb) {
if (!Array.isArray(channels)) { return cb('INVALID_LIST'); }
var L = channels.length;
var sem = Saferphore.create(10);
var absentees = [];
var job = function (channel, wait) {
return function (give) {
getFileSize(Env, channel, wait(give(function (e, size) {
if (e) { return; }
if (size === 0) { absentees.push(channel); }
})));
};
};
nThen(function (w) {
for (var i = 0; i < L; i++) {
sem.take(job(channels[i], w));
}
}).nThen(function () {
cb(void 0, absentees);
});
};
var getTotalSize = function (Env, publicKey, cb) { var getTotalSize = function (Env, publicKey, cb) {
var bytes = 0; var bytes = 0;
return void getChannelList(Env, publicKey, function (channels) { return void getChannelList(Env, publicKey, function (channels) {
@ -1004,7 +1037,8 @@ var isUnauthenticatedCall = function (call) {
'GET_MULTIPLE_FILE_SIZE', 'GET_MULTIPLE_FILE_SIZE',
'IS_CHANNEL_PINNED', 'IS_CHANNEL_PINNED',
'IS_NEW_CHANNEL', 'IS_NEW_CHANNEL',
'GET_HISTORY_OFFSET' 'GET_HISTORY_OFFSET',
'GET_DELETED_PADS',
].indexOf(call) !== -1; ].indexOf(call) !== -1;
}; };
@ -1133,6 +1167,14 @@ RPC.create = function (
} }
respond(e, [null, dict, null]); respond(e, [null, dict, null]);
}); });
case 'GET_DELETED_PADS':
return void getDeletedPads(Env, msg[1], function (e, list) {
if (e) {
WARN(e, msg[1]);
return respond(e);
}
respond(e, [null, list, null]);
});
case 'IS_CHANNEL_PINNED': case 'IS_CHANNEL_PINNED':
return void isChannelPinned(Env, msg[1], function (isPinned) { return void isChannelPinned(Env, msg[1], function (isPinned) {
respond(null, [null, isPinned, null]); respond(null, [null, isPinned, null]);

@ -9,6 +9,7 @@ var WebSocketServer = require('ws').Server;
var NetfluxSrv = require('./node_modules/chainpad-server/NetfluxWebsocketSrv'); var NetfluxSrv = require('./node_modules/chainpad-server/NetfluxWebsocketSrv');
var Package = require('./package.json'); var Package = require('./package.json');
var Path = require("path"); var Path = require("path");
var nThen = require("nthen");
var config; var config;
try { try {
@ -213,8 +214,29 @@ if (config.httpSafePort) {
var wsConfig = { server: httpServer }; var wsConfig = { server: httpServer };
var createSocketServer = function (err, rpc) { var rpc;
if(!config.useExternalWebsocket) {
var nt = nThen(function (w) {
if (!config.enableTaskScheduling) { return; }
var Tasks = require("./storage/tasks");
console.log("loading task scheduler");
Tasks.create(config, w(function (e, tasks) {
config.tasks = tasks;
}));
}).nThen(function (w) {
config.rpc = typeof(config.rpc) === 'undefined'? './rpc.js' : config.rpc;
if (typeof(config.rpc) !== 'string') { return; }
// load pin store...
var Rpc = require(config.rpc);
Rpc.create(config, debuggable, w(function (e, _rpc) {
if (e) {
w.abort();
throw e;
}
rpc = _rpc;
}));
}).nThen(function () {
if(config.useExternalWebsocket) { return; }
if (websocketPort !== config.httpPort) { if (websocketPort !== config.httpPort) {
console.log("setting up a new websocket server"); console.log("setting up a new websocket server");
wsConfig = { port: websocketPort}; wsConfig = { port: websocketPort};
@ -223,25 +245,7 @@ var createSocketServer = function (err, rpc) {
Storage.create(config, function (store) { Storage.create(config, function (store) {
NetfluxSrv.run(store, wsSrv, config, rpc); NetfluxSrv.run(store, wsSrv, config, rpc);
}); });
}
};
var loadRPC = function (cb) {
config.rpc = typeof(config.rpc) === 'undefined'? './rpc.js' : config.rpc;
if (typeof(config.rpc) === 'string') {
// load pin store...
var Rpc = require(config.rpc);
Rpc.create(config, debuggable, function (e, rpc) {
if (e) { throw e; }
cb(void 0, rpc);
}); });
} else {
cb();
}
};
loadRPC(createSocketServer);
if (config.debugReplName) { if (config.debugReplName) {
require('replify')({ name: config.debugReplName, app: debuggableStore }); require('replify')({ name: config.debugReplName, app: debuggableStore });

@ -0,0 +1,94 @@
var Fs = require("fs");
var Path = require("path");
var nacl = require("tweetnacl");
var nThen = require("nthen");
var Tasks = module.exports;
var encode = function (time, command, args) {
if (typeof(time) !== 'number') { return null; }
if (typeof(command) !== 'string') { return null; }
if (!Array.isArray(args)) { return [time, command]; }
return [time, command].concat(args);
};
var randomId = function () {
var bytes = Array.prototype.slice.call(nacl.randomBytes(16));
return bytes.map(function (b) {
var n = Number(b & 0xff).toString(16);
return n.length === 1? '0' + n: n;
}).join('');
};
var mkPath = function (env, id) {
return Path.join(env.root, id.slice(0, 2), id) + '.ndjson';
};
var getFreeId = function (env, cb, tries) {
if (tries > 5) { return void cb('ETOOMANYTRIES'); }
// generate a unique id
var id = randomId();
// derive a path from that id
var path = mkPath(env, id);
Fs.stat(path, function (err) {
if (err && err.code === "ENOENT") {
cb(void 0, id);
} else {
getFreeId(env, cb);
}
});
};
var write = function (env, task, cb) {
var str = JSON.stringify(task) + '\n';
var id = nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(str))).replace(/\//g, '-');
var path = mkPath(env, id);
nThen(function (w) {
// check if the file already exists...
Fs.stat(path, w(function (err) {
if (err && err.code === 'ENOENT') { return; }
w.abort(); cb();
}));
}).nThen(function (w) {
// create the parent directory if it does not exist
var dir = id.slice(0, 2);
var dirpath = Path.join(env.root, dir);
Fs.mkdir(dirpath, 0x1ff, w(function (err) {
if (err && err.code !== 'EEXIST') {
return void cb(err);
}
}));
}).nThen(function () {
// write the file to the path
Fs.writeFile(mkPath(env, id), str, function (e) {
if (e) { return void cb(e); }
cb();
});
});
};
Tasks.create = function (config, cb) {
var env = {
root: config.taskPath || './tasks',
};
// make sure the path exists...
Fs.mkdir(env.root, 0x1ff, function (err) {
if (err && err.code !== 'EEXIST') {
throw err;
}
cb(void 0, {
write: function (time, command, args, cb) {
var task = encode(time, command, args);
write(env, task, cb);
},
});
});
};

@ -42,6 +42,11 @@ define(function() {
'#800080', // purple '#800080', // purple
]; ];
// Background color in the apps with centered content:
// - file app in view mode
// - rich text app when editor's width reduced in settings
config.appBackgroundColor = '#666';
// Set enableTemplates to false to remove the button allowing users to save a pad as a template // Set enableTemplates to false to remove the button allowing users to save a pad as a template
// and remove the template category in CryptDrive // and remove the template category in CryptDrive
config.enableTemplates = true; config.enableTemplates = true;

@ -6,7 +6,7 @@ define([
], function (Util, Messages, Crypto) { ], function (Util, Messages, Crypto) {
var Nacl = window.nacl; var Nacl = window.nacl;
var Hash = {}; var Hash = window.CryptPad_Hash = {};
var uint8ArrayToHex = Util.uint8ArrayToHex; var uint8ArrayToHex = Util.uint8ArrayToHex;
var hexToBase64 = Util.hexToBase64; var hexToBase64 = Util.hexToBase64;
@ -176,7 +176,7 @@ Version 1
secret.keys = Crypto.createEditCryptor(); secret.keys = Crypto.createEditCryptor();
secret.key = Crypto.createEditCryptor().editKeyStr; secret.key = Crypto.createEditCryptor().editKeyStr;
}; };
if (!secretHash && !/#/.test(window.location.href)) { if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
generate(); generate();
return secret; return secret;
} else { } else {
@ -300,6 +300,8 @@ Version 1
var rHref = href || getRelativeHref(window.location.href); var rHref = href || getRelativeHref(window.location.href);
var parsed = parsePadUrl(rHref); var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
// We can't have a stronger hash if we're already in edit mode
if (parsed.hashData && parsed.hashData.mode === 'edit') { return; }
var stronger; var stronger;
Object.keys(recents).some(function (id) { Object.keys(recents).some(function (id) {
var pad = recents[id]; var pad = recents[id];

@ -317,11 +317,11 @@ define([
message = dialog.message(msg); message = dialog.message(msg);
} }
var close = Util.once(function (el) { var close = function (el) {
var $el = $(el).fadeOut(150, function () { var $el = $(el).fadeOut(150, function () {
$el.remove(); $el.detach();
});
}); });
};
var navs = []; var navs = [];
opt.buttons.forEach(function (b) { opt.buttons.forEach(function (b) {
@ -329,7 +329,7 @@ define([
var button = h('button', { tabindex: '1', 'class': b.className || '' }, b.name); var button = h('button', { tabindex: '1', 'class': b.className || '' }, b.name);
$(button).click(function () { $(button).click(function () {
b.onClick(); b.onClick();
close($(this).parents('.alertify').first()); close($(button).parents('.alertify').first());
}); });
if (b.keys && b.keys.length) { $(button).attr('data-keys', JSON.stringify(b.keys)); } if (b.keys && b.keys.length) { $(button).attr('data-keys', JSON.stringify(b.keys)); }
navs.push(button); navs.push(button);

@ -1816,6 +1816,13 @@ define([
$button.click(function () { $button.click(function () {
create(); create();
}); });
// Settings button
var origin = common.getMetadataMgr().getPrivateData().origin;
$(h('div.cp-creation-settings', h('a', {
href: origin + '/settings/#creation',
target: '_blank'
}, Messages.creation_settings))).appendTo($creation);
}; };
return UIElements; return UIElements;

@ -1,6 +1,6 @@
(function (window) { (function (window) {
define([], function () { define([], function () {
var Util = {}; var Util = window.CryptPad_Util = {};
// If once is true, after the event has been fired, any further handlers which are // If once is true, after the event has been fired, any further handlers which are
// registered will fire immediately, and this type of event cannot be fired twice. // registered will fire immediately, and this type of event cannot be fired twice.

@ -195,6 +195,16 @@ define([
common.clearOwnedChannel = function (channel, cb) { common.clearOwnedChannel = function (channel, cb) {
postMessage("CLEAR_OWNED_CHANNEL", channel, cb); postMessage("CLEAR_OWNED_CHANNEL", channel, cb);
}; };
common.removeOwnedChannel = function (channel, cb) {
postMessage("REMOVE_OWNED_CHANNEL", channel, cb);
};
common.getDeletedPads = function (cb) {
postMessage("GET_DELETED_PADS", null, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.uploadComplete = function (cb) { common.uploadComplete = function (cb) {
postMessage("UPLOAD_COMPLETE", null, function (obj) { postMessage("UPLOAD_COMPLETE", null, function (obj) {
@ -550,6 +560,11 @@ define([
return void cb(null, hashes); return void cb(null, hashes);
} }
if (hashes.editHash) {
// no need to find stronger if we already have edit hash
return void cb(null, hashes);
}
postMessage("GET_STRONGER_HASH", { postMessage("GET_STRONGER_HASH", {
href: window.location.href href: window.location.href
}, function (hash) { }, function (hash) {

@ -94,9 +94,27 @@ define([
return list; return list;
}; };
var getCanonicalChannelList = function () { var getExpirableChannelList = function () {
return Util.deduplicateString(getUserChannelList()).sort(); var list = [];
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
var data = store.userObject.getFileData(id);
var edPublic = store.proxy.edPublic;
// Push channels owned by someone else or channel that should have expired
// because of the expiration time
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
(data.expire && data.expire < (+new Date()))) {
list.push(Hash.hrefToHexChannelId(data.href));
}
});
return list;
}; };
var getCanonicalChannelList = function (expirable) {
var list = expirable ? getExpirableChannelList() : getUserChannelList();
return Util.deduplicateString(list).sort();
};
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
/////////////////////// RPC ////////////////////////////////////// /////////////////////// RPC //////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@ -172,6 +190,34 @@ define([
}); });
}; };
Store.removeOwnedChannel = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.removeOwnedChannel(data, function (err) {
cb({error:err});
});
};
var arePinsSynced = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList(false);
var local = Hash.hashChannelList(list);
store.rpc.getServerHash(function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash === local);
});
};
var resetPins = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList(false);
store.rpc.reset(list, function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash);
});
};
Store.uploadComplete = function (data, cb) { Store.uploadComplete = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadComplete(function (err, res) { store.rpc.uploadComplete(function (err, res) {
@ -196,27 +242,6 @@ define([
}); });
}; };
var arePinsSynced = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList();
var local = Hash.hashChannelList(list);
store.rpc.getServerHash(function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash === local);
});
};
var resetPins = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList();
store.rpc.reset(list, function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash);
});
};
Store.uploadChunk = function (data, cb) { Store.uploadChunk = function (data, cb) {
store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) { store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) {
cb({ cb({
@ -309,6 +334,23 @@ define([
}); });
}; };
Store.getDeletedPads = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var list = getCanonicalChannelList(true);
if (!Array.isArray(list)) {
return void cb({error: 'INVALID_FILE_LIST'});
}
store.anon_rpc.send('GET_DELETED_PADS', list, function (e, res) {
if (e) { return void cb({error: e}); }
if (res && res.length && Array.isArray(res[0])) {
cb(res[0]);
} else {
cb({error: 'UNEXPECTED_RESPONSE'});
}
});
};
Store.initAnonRpc = function (data, cb) { Store.initAnonRpc = function (data, cb) {
require([ require([
'/common/rpc.js', '/common/rpc.js',
@ -321,8 +363,6 @@ define([
}); });
}; };
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
/////////////////////// Store //////////////////////////////////// /////////////////////// Store ////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@ -579,6 +619,8 @@ define([
contains = true; contains = true;
pad.atime = +new Date(); pad.atime = +new Date();
pad.title = title; pad.title = title;
pad.owners = owners;
pad.expire = expire;
// If the href is different, it means we have a stronger one // If the href is different, it means we have a stronger one
if (href !== pad.href) { isStronger = true; } if (href !== pad.href) { isStronger = true; }
@ -871,6 +913,8 @@ define([
var userObject = store.userObject = UserObject.init(proxy.drive, { var userObject = store.userObject = UserObject.init(proxy.drive, {
pinPads: Store.pinPads, pinPads: Store.pinPads,
unpinPads: Store.unpinPads, unpinPads: Store.unpinPads,
removeOwnedChannel: Store.removeOwnedChannel,
edPublic: store.proxy.edPublic,
loggedIn: store.loggedIn, loggedIn: store.loggedIn,
log: function (msg) { log: function (msg) {
postMessage("DRIVE_LOG", msg); postMessage("DRIVE_LOG", msg);

@ -57,27 +57,11 @@ define([], function () {
// shim between chainpad and netflux // shim between chainpad and netflux
var msgIn = function (peerId, msg) { var msgIn = function (peerId, msg) {
return msg.replace(/^cp\|/, ''); return msg.replace(/^cp\|/, '');
/*try {
var decryptedMsg = Crypto.decrypt(msg, validateKey);
return decryptedMsg;
} catch (err) {
console.error(err);
return msg;
}*/
}; };
var msgOut = function (msg) { var msgOut = function (msg) {
if (readOnly) { return; } if (readOnly) { return; }
return msg; return msg;
/*try {
var cmsg = Crypto.encrypt(msg);
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
return cmsg;
} catch (err) {
console.log(msg);
throw err;
}*/
}; };
var onMsg = function(peer, msg, wc, network, direct) { var onMsg = function(peer, msg, wc, network, direct) {
@ -93,7 +77,9 @@ define([], function () {
if (parsed.channel === wc.id && !validateKey) { if (parsed.channel === wc.id && !validateKey) {
validateKey = parsed.validateKey; validateKey = parsed.validateKey;
} }
if (parsed.channel === wc.id) {
padData = parsed; padData = parsed;
}
// We have to return even if it is not the current channel: // We have to return even if it is not the current channel:
// we don't want to continue with other channels messages here // we don't want to continue with other channels messages here
return; return;

@ -31,6 +31,9 @@ define([
case 'CLEAR_OWNED_CHANNEL': { case 'CLEAR_OWNED_CHANNEL': {
Store.clearOwnedChannel(data, cb); break; Store.clearOwnedChannel(data, cb); break;
} }
case 'REMOVE_OWNED_CHANNEL': {
Store.removeOwnedChannel(data, cb); break;
}
case 'UPLOAD_CHUNK': { case 'UPLOAD_CHUNK': {
Store.uploadChunk(data, cb); break; Store.uploadChunk(data, cb); break;
} }
@ -49,6 +52,9 @@ define([
case 'UNPIN_PADS': { case 'UNPIN_PADS': {
Store.unpinPads(data, cb); break; Store.unpinPads(data, cb); break;
} }
case 'GET_DELETED_PADS': {
Store.getDeletedPads(data, cb); break;
}
case 'GET_PINNED_USAGE': { case 'GET_PINNED_USAGE': {
Store.getPinnedUsage(data, cb); break; Store.getPinnedUsage(data, cb); break;
} }

@ -17,8 +17,12 @@ define([
console.error("unpinPads was not provided"); console.error("unpinPads was not provided");
}; };
var pinPads = config.pinPads; var pinPads = config.pinPads;
var removeOwnedChannel = config.removeOwnedChannel || function () {
console.error("removeOwnedChannel was not provided");
};
var loggedIn = config.loggedIn; var loggedIn = config.loggedIn;
var workgroup = config.workgroup; var workgroup = config.workgroup;
var edPublic = config.edPublic;
var ROOT = exp.ROOT; var ROOT = exp.ROOT;
var FILES_DATA = exp.FILES_DATA; var FILES_DATA = exp.FILES_DATA;
@ -90,9 +94,14 @@ define([
exp.getFiles([FILES_DATA]).forEach(function (id) { exp.getFiles([FILES_DATA]).forEach(function (id) {
if (filesList.indexOf(id) === -1) { if (filesList.indexOf(id) === -1) {
var fd = exp.getFileData(id); var fd = exp.getFileData(id);
if (fd && fd.href) { var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href);
toClean.push(Hash.hrefToHexChannelId(fd.href)); // If trying to remove an owned pad, remove it from server also
if (fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
removeOwnedChannel(channelId, function (obj) {
if (obj && obj.error) { console.error(obj.error); }
});
} }
if (channelId) { toClean.push(channelId); }
spliceFileData(id); spliceFileData(id);
} }
}); });

@ -145,7 +145,22 @@ define([
if (response && response.length) { if (response && response.length) {
cb(void 0, response[0]); cb(void 0, response[0]);
} else { } else {
cb(); cb('INVALID_RESPONSE');
}
});
};
exp.removeOwnedChannel = function (channel, cb) {
if (typeof(channel) !== 'string' || channel.length !== 32) {
// can't use this on files because files can't be owned...
return void cb('INVALID_ARGUMENTS');
}
rpc.send('REMOVE_OWNED_CHANNEL', channel, function (e, response) {
if (e) { return void cb(e); }
if (response && response.length) {
cb(void 0, response[0]); // I haven't tested this...
} else {
cb('INVALID_RESPONSE');
} }
}); });
}; };

@ -501,6 +501,9 @@ define([
sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) { sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) {
Cryptpad.clearOwnedChannel(channel, cb); Cryptpad.clearOwnedChannel(channel, cb);
}); });
sframeChan.on('Q_REMOVE_OWNED_CHANNEL', function (channel, cb) {
Cryptpad.removeOwnedChannel(channel, cb);
});
if (cfg.addRpc) { if (cfg.addRpc) {
cfg.addRpc(sframeChan, Cryptpad, Utils); cfg.addRpc(sframeChan, Cryptpad, Utils);

@ -392,15 +392,12 @@ define([
UI.log(data.logText); UI.log(data.logText);
}); });
ctx.metadataMgr.onChange(function () { ctx.metadataMgr.onReady(waitFor());
}).nThen(function () {
try { try {
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed; var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
Feedback.init(feedback); Feedback.init(feedback);
} catch (e) { Feedback.init(false); } } catch (e) { Feedback.init(false); }
});
ctx.metadataMgr.onReady(waitFor());
}).nThen(function () {
ctx.sframeChan.ready(); ctx.sframeChan.ready();
cb(funcs); cb(funcs);
}); });

@ -201,12 +201,17 @@ define({
// Inner drive needs to send command and receive updates from the async store // Inner drive needs to send command and receive updates from the async store
'Q_DRIVE_USEROBJECT': true, 'Q_DRIVE_USEROBJECT': true,
'Q_DRIVE_GETOBJECT': true, 'Q_DRIVE_GETOBJECT': true,
// Get the pads deleted from the server by other users to remove them from the drive
'Q_DRIVE_GETDELETED': true,
// Store's userObject need to send log messages to inner to display them in the UI // Store's userObject need to send log messages to inner to display them in the UI
'EV_DRIVE_LOG': true, 'EV_DRIVE_LOG': true,
// Refresh the drive when the drive has changed ('change' or 'remove' events) // Refresh the drive when the drive has changed ('change' or 'remove' events)
'EV_DRIVE_CHANGE': true, 'EV_DRIVE_CHANGE': true,
'EV_DRIVE_REMOVE': true, 'EV_DRIVE_REMOVE': true,
// Remove an owned pad from the server
'Q_REMOVE_OWNED_CHANNEL': true,
// Notifications about connection and disconnection from the network // Notifications about connection and disconnection from the network
'EV_NETWORK_DISCONNECT': true, 'EV_NETWORK_DISCONNECT': true,
'EV_NETWORK_RECONNECT': true, 'EV_NETWORK_RECONNECT': true,

@ -380,6 +380,18 @@ define([
var trashpaths = _findFileInTrash([TRASH], file); var trashpaths = _findFileInTrash([TRASH], file);
return rootpaths.concat(templatepaths, trashpaths); return rootpaths.concat(templatepaths, trashpaths);
}; };
// Get drive ids of files from their channel ids
exp.findChannels = function (channels) {
var allFilesList = files[FILES_DATA];
var channels64 = channels.slice().map(Util.hexToBase64);
return getFiles([FILES_DATA]).filter(function (k) {
var data = allFilesList[k];
var parsed = Hash.parsePadUrl(data.href);
return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1;
});
};
exp.search = function (value) { exp.search = function (value) {
if (typeof(value) !== "string") { return []; } if (typeof(value) !== "string") { return []; }
value = value.trim(); value = value.trim();

@ -802,6 +802,7 @@ define([
hide.push('properties'); hide.push('properties');
hide.push('rename'); hide.push('rename');
hide.push('openparent'); hide.push('openparent');
hide.push('hashtag');
} }
if (containsFolder && paths.length > 1) { if (containsFolder && paths.length > 1) {
// Cannot open multiple folders // Cannot open multiple folders
@ -911,6 +912,7 @@ define([
//var actions = []; //var actions = [];
var toShow = filterContextMenu(menuType, paths); var toShow = filterContextMenu(menuType, paths);
var $actions = $contextMenu.find('a'); var $actions = $contextMenu.find('a');
$contextMenu.data('paths', paths);
$actions = $actions.filter(function (i, el) { $actions = $actions.filter(function (i, el) {
return toShow.some(function (className) { return $(el).is(className); }); return toShow.some(function (className) { return $(el).is(className); });
}); });
@ -922,9 +924,6 @@ define([
} else { } else {
$a.text($(el).text()); $a.text($(el).text());
} }
$(el).data('paths', paths);
//$(el).data('path', path);
//:$(el).data('element', $element);
$container.append($a); $container.append($a);
$a.click(function() { $(el).click(); }); $a.click(function() { $(el).click(); });
}); });
@ -1435,6 +1434,7 @@ define([
case FILES_DATA: pName = FILES_DATA_NAME; break; case FILES_DATA: pName = FILES_DATA_NAME; break;
case SEARCH: pName = SEARCH_NAME; break; case SEARCH: pName = SEARCH_NAME; break;
case RECENT: pName = RECENT_NAME; break; case RECENT: pName = RECENT_NAME; break;
case OWNED: pName = OWNED_NAME; break;
default: pName = name; default: pName = name;
} }
return pName; return pName;
@ -1509,6 +1509,11 @@ define([
if (!APP.loggedIn) { if (!APP.loggedIn) {
msg = Messages.fm_info_anonymous; msg = Messages.fm_info_anonymous;
$box.html(msg); $box.html(msg);
$box.find('a[target!="_blank"]').click(function (e) {
e.preventDefault();
var href = $(this).attr('href');
common.gotoURL(href);
});
return $box; return $box;
} }
if (!msg || APP.store['hide-info-' + path[0]] === '1') { if (!msg || APP.store['hide-info-' + path[0]] === '1') {
@ -2682,11 +2687,13 @@ define([
$contextMenu.find('.cp-app-drive-context-delete').text(Messages.fc_remove) $contextMenu.find('.cp-app-drive-context-delete').text(Messages.fc_remove)
.attr('data-icon', 'fa-eraser'); .attr('data-icon', 'fa-eraser');
} }
var deletePaths = function (paths) { var deletePaths = function (paths, pathsList) {
var pathsList = []; pathsList = pathsList || [];
if (paths) {
paths.forEach(function (p) { pathsList.push(p.path); }); paths.forEach(function (p) { pathsList.push(p.path); });
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); }
if (paths.length === 1) { var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [pathsList.length]);
if (pathsList.length === 1) {
msg = Messages.fm_removePermanentlyDialog; msg = Messages.fm_removePermanentlyDialog;
} }
UI.confirm(msg, function(res) { UI.confirm(msg, function(res) {
@ -2695,6 +2702,33 @@ define([
filesOp.delete(pathsList, refresh); filesOp.delete(pathsList, refresh);
}); });
}; };
var deleteOwnedPaths = function (paths, pathsList) {
pathsList = pathsList || [];
if (paths) {
paths.forEach(function (p) { pathsList.push(p.path); });
}
var msgD = pathsList.length === 1 ? Messages.fm_deleteOwnedPad :
Messages.fm_deleteOwnedPads;
UI.confirm(msgD, function(res) {
$(window).focus();
if (!res) { return; }
// Try to delete each selected pad from server, and delete from drive if no error
var n = nThen(function () {});
pathsList.forEach(function (p) {
var el = filesOp.find(p);
var data = filesOp.getFileData(el);
var parsed = Hash.parsePadUrl(data.href);
var channel = Util.base64ToHex(parsed.hashData.channel);
n = n.nThen(function (waitFor) {
sframeChan.query('Q_REMOVE_OWNED_CHANNEL', channel,
waitFor(function (e) {
if (e) { return void console.error(e); }
filesOp.delete([p], refresh);
}));
});
});
});
};
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {
e.stopPropagation(); e.stopPropagation();
var paths = $contextMenu.data('paths'); var paths = $contextMenu.data('paths');
@ -2720,27 +2754,7 @@ define([
moveElements(pathsList, [TRASH], false, refresh); moveElements(pathsList, [TRASH], false, refresh);
} }
else if ($(this).hasClass('cp-app-drive-context-deleteowned')) { else if ($(this).hasClass('cp-app-drive-context-deleteowned')) {
var msgD = Messages.fm_deleteOwnedPads; deleteOwnedPaths(paths);
UI.confirm(msgD, function(res) {
$(window).focus();
if (!res) { return; }
// Try to delete each selected pad from server, and delete from drive if no error
var n = nThen(function () {});
paths.forEach(function (p) {
var el = filesOp.find(p.path);
var data = filesOp.getFileData(el);
var parsed = Hash.parsePadUrl(data.href);
var channel = Util.base64ToHex(parsed.hashData.channel);
n = n.nThen(function (waitFor) {
sframeChan.query('Q_CONTACTS_CLEAR_OWNED_CHANNEL', channel,
waitFor(function (e) {
if (e) { return void console.error(e); }
filesOp.delete([p.path], refresh);
}));
});
});
});
return;
} }
else if ($(this).hasClass('cp-app-drive-context-open')) { else if ($(this).hasClass('cp-app-drive-context-open')) {
paths.forEach(function (p) { paths.forEach(function (p) {
@ -2878,18 +2892,11 @@ define([
if (!$(elmt).data('path')) { return; } if (!$(elmt).data('path')) { return; }
paths.push($(elmt).data('path')); paths.push($(elmt).data('path'));
}); });
// If we are in the trash or anon pad or if we are holding the "shift" key, delete permanently, if (!paths.length) { return; }
// If we are in the trash or anon pad or if we are holding the "shift" key,
// delete permanently
if (!APP.loggedIn || isTrash || e.shiftKey) { if (!APP.loggedIn || isTrash || e.shiftKey) {
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); deletePaths(null, paths);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
filesOp.delete(paths, refresh);
});
return; return;
} }
// else move to trash // else move to trash
@ -2975,6 +2982,19 @@ define([
refresh(); refresh();
UI.removeLoadingScreen(); UI.removeLoadingScreen();
sframeChan.query('Q_DRIVE_GETDELETED', null, function (err, data) {
var ids = filesOp.findChannels(data);
var titles = [];
ids.forEach(function (id) {
var title = filesOp.getTitle(id);
titles.push(title);
var paths = filesOp.findFile(id);
filesOp.delete(paths, refresh);
});
if (!titles.length) { return; }
UI.log(Messages._getKey('fm_deletedPads', [titles.join(', ')]));
});
}; };
var setHistory = function (bool, update) { var setHistory = function (bool, update) {
@ -3091,7 +3111,6 @@ define([
throw new Error("Corrupted drive"); throw new Error("Corrupted drive");
} }
andThen(common, proxy); andThen(common, proxy);
UI.removeLoadingScreen();
var onDisconnect = APP.onDisconnect = function (noAlert) { var onDisconnect = APP.onDisconnect = function (noAlert) {
setEditable(false); setEditable(false);

@ -56,6 +56,12 @@ define([
cb(obj); cb(obj);
}); });
}); });
sframeChan.on('Q_DRIVE_GETDELETED', function (data, cb) {
Cryptpad.getDeletedPads(function (err, obj) {
if (err) { return void console.error(err); }
cb(obj);
});
});
Cryptpad.onNetworkDisconnect.reg(function () { Cryptpad.onNetworkDisconnect.reg(function () {
sframeChan.event('EV_NETWORK_DISCONNECT'); sframeChan.event('EV_NETWORK_DISCONNECT');
}); });

@ -224,6 +224,8 @@ define([
if (decrypting) { return; } if (decrypting) { return; }
decrypting = true; decrypting = true;
displayFile(ev, sizeMb, function (err) { displayFile(ev, sizeMb, function (err) {
$appContainer.css('background-color',
common.getAppConfig().appBackgroundColor);
if (err) { UI.alert(err); } if (err) { UI.alert(err); }
}); });
}; };

@ -31,6 +31,7 @@ define([
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-util.js', '/common/common-util.js',
'/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad/chainpad.dist.js',
'/customize/application_config.js',
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
@ -50,7 +51,8 @@ define([
ApiConfig, ApiConfig,
Hash, Hash,
Util, Util,
ChainPad) ChainPad,
AppConfig)
{ {
var DiffDom = window.diffDOM; var DiffDom = window.diffDOM;
@ -592,7 +594,8 @@ define([
} }
// Used in ckeditor-config.js // Used in ckeditor-config.js
Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs; Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
var newCss = '.cke_body_width { background: #666; height: 100%; }' + var backColor = AppConfig.appBackgroundColor;
var newCss = '.cke_body_width { background: '+ backColor +'; height: 100%; }' +
'.cke_body_width body {' + '.cke_body_width body {' +
'max-width: 50em; padding: 10px 30px; margin: 0 auto; min-height: 100%;'+ 'max-width: 50em; padding: 10px 30px; margin: 0 auto; min-height: 100%;'+
'box-sizing: border-box;'+ 'box-sizing: border-box;'+

@ -75,7 +75,7 @@ define([
} }
}; };
if (!AppConfig.dislayCreationScreen) { if (!AppConfig.displayCreationScreen) {
delete categories.creation; delete categories.creation;
} }
if (AppConfig.disableFeedback) { if (AppConfig.disableFeedback) {
@ -789,7 +789,7 @@ define([
var $categories = $('<div>', {'class': 'cp-sidebarlayout-categories'}) var $categories = $('<div>', {'class': 'cp-sidebarlayout-categories'})
.appendTo(APP.$leftside); .appendTo(APP.$leftside);
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside); APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
var active = 'account'; var active = privateData.category || 'account';
Object.keys(categories).forEach(function (key) { Object.keys(categories).forEach(function (key) {
var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories); var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories);
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); } if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }

@ -66,9 +66,18 @@ define([
Cryptpad.mergeAnonDrive(cb); Cryptpad.mergeAnonDrive(cb);
}); });
}; };
var category;
if (window.location.hash) {
category = window.location.hash.slice(1);
window.location.hash = '';
}
var addData = function (obj) {
if (category) { obj.category = category; }
};
SFCommonO.start({ SFCommonO.start({
noRealtime: true, noRealtime: true,
addRpc: addRpc addRpc: addRpc,
addData: addData
}); });
}); });
}); });

@ -70,6 +70,10 @@ define([
var makeCheckbox = function (id, cb) { var makeCheckbox = function (id, cb) {
var entry = APP.lm.proxy.data[id]; var entry = APP.lm.proxy.data[id];
if (!entry || typeof(entry) !== 'object') {
return void console.log('entry undefined');
}
var checked = entry.state === 1 ? var checked = entry.state === 1 ?
'cp-app-todo-task-checkbox-checked fa-check-square-o': 'cp-app-todo-task-checkbox-checked fa-check-square-o':
'cp-app-todo-task-checkbox-unchecked fa-square-o'; 'cp-app-todo-task-checkbox-unchecked fa-square-o';
@ -108,6 +112,9 @@ define([
.appendTo($taskDiv); .appendTo($taskDiv);
var entry = APP.lm.proxy.data[el]; var entry = APP.lm.proxy.data[el];
if (!entry || typeof(entry) !== 'object') {
return void console.log('entry undefined');
}
if (entry.state) { if (entry.state) {
$taskDiv.addClass('cp-app-todo-task-complete'); $taskDiv.addClass('cp-app-todo-task-complete');

@ -32,6 +32,7 @@ define([
sFrameChan.onReady(waitFor()); sFrameChan.onReady(waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var $container = $('#cp-app-worker-container'); var $container = $('#cp-app-worker-container');
$('<a>', {href:'http://localhost:3000/worker/', target:'_blank'}).text('other').appendTo($container);
var $bar = $('.cp-toolbar-container'); var $bar = $('.cp-toolbar-container');
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle']; var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];

@ -29,16 +29,17 @@ require.config({
}); });
var i = 0; var i = 0;
var id = Math.floor(Math.random()*100000);
onconnect = function(e) { onconnect = function(e) {
console.log(e); console.log(e);
console.log(i); console.log(i);
var port = e.ports[0]; var port = e.ports[0];
console.log('here'); console.log('here');
require([ //require([
'/common/outer/async-store.js' // '/common/outer/async-store.js'
], function (Store) { //], function (Store) {
console.log(Store); //console.log(Store);
console.log(self.Proxy); console.log(self.Proxy);
var n = i; var n = i;
port.postMessage({state: 'READY'}); port.postMessage({state: 'READY'});
@ -46,8 +47,9 @@ onconnect = function(e) {
console.log('worker received'); console.log('worker received');
console.log(e.data); console.log(e.data);
port.postMessage('hello CryptPad'+n); port.postMessage('hello CryptPad'+n);
port.postMessage('This is '+id);
}; };
var data = { /*var data = {
query: function (cmd, data, cb) { query: function (cmd, data, cb) {
console.log(cmd, data); console.log(cmd, data);
}, },
@ -64,7 +66,7 @@ onconnect = function(e) {
}); });
port.postMessage('Store is connected!'); port.postMessage('Store is connected!');
port.postMessage('Your username is ' + ret.store.proxy['cryptpad.username']); port.postMessage('Your username is ' + ret.store.proxy['cryptpad.username']);
}); });*/
i++; i++;
}); //});
}; };

Loading…
Cancel
Save