Merge branch 'staging' into framework

pull/1/head
Caleb James DeLisle 7 years ago
commit 753e2d797e

@ -76,7 +76,7 @@ define([
])
])
]),
h('div.cp-version-footer', "CryptPad v1.16.0 (Qalupalik)")
h('div.cp-version-footer', "CryptPad v1.17.0 (Ratatoskr)")
]);
};
@ -524,8 +524,8 @@ define([
h('div.row.cp-register-test',[
h('hr'),
h('div.col-12', [
setHTML(h('p.test-details'), Msg.register_testimonial),
h('a.cp-test-source.pull-right', { href : 'http://boingboing.net/2016/09/26/cryptpad-a-freeopen-end-to.html'}, Msg.register_testimonial_name)
setHTML(h('p.test-details'), " \"Tools like Etherpad and Google Docs [...] all share a weakness, which is that whomever owns the document server can see everything you're typing. Cryptpad is a free/open project that uses some of the ideas behind blockchain to implement a \"zero-knowledge\" version of a collaborative document editor, ensuring that only the people working on a document can see it.\" "),
h('a.cp-test-source.pull-right', { href : 'http://boingboing.net/2016/09/26/cryptpad-a-freeopen-end-to.html'}, "Cory Doctorow")
])
])
]),
@ -558,8 +558,7 @@ define([
placeholder: Msg.login_password,
}),
h('div.extra', [
h('button.login.first.btn', Msg.login_login),
h('button#register.btn.register.cp-login-register', Msg.login_register)
h('button.login.first.btn', Msg.login_login)
])
])
]),
@ -657,7 +656,7 @@ define([
h('br')
]),
h('div#cp-app-poll-table-container', [
h('div#cp-app-poll-table-scroll'),
h('div#cp-app-poll-table-scroll', [h('table')]),
h('button#cp-app-poll-create-user.btn.btn-secondary', {
title: Msg.poll_create_user
}, h('span.fa.fa-plus')),
@ -669,9 +668,12 @@ define([
h('h2#cp-app-poll-comments-add-title', Msg.poll_comment_add),
h('div#cp-app-poll-comments-add', [
h('input.cp-app-poll-comments-add-name', {
type: 'text'
type: 'text',
placeholder: Msg.anonymous
}),
h('textarea.cp-app-poll-comments-add-msg', {
placeholder: Msg.poll_comment_placeholder
}),
h('textarea.cp-app-poll-comments-add-msg'),
h('button.cp-app-poll-comments-add-submit.btn.btn-secondary',
Msg.poll_comment_submit),
h('button.cp-app-poll-comments-add-cancel.btn.btn-secondary',
@ -679,7 +681,8 @@ define([
]),
h('h2#cp-app-poll-comments-list-title', Msg.poll_comment_list),
h('div#cp-app-poll-comments-list')
])
]),
h('div#cp-app-poll-nocomments', Msg.poll_comment_disabled)
])
])
])

@ -54,19 +54,11 @@
}
.extra {
margin-top: 1em;
.cp-login-register {
color: @cryptpad_color_blue;
background: #fff;
border: 2px solid @cryptpad_color_blue;
border-radius: 0;
&:hover {
transform: scale(1.05);
}
}
.login {
background: transparent;
color: @cryptpad_color_blue;
padding: 0;
background: @cryptpad_color_blue;
color: #fff;
padding: 10px;
border-radius: 0;
&:hover {
transform: scale(1.05);
}

@ -156,8 +156,10 @@ define(function () {
out.filePicker_filter = "Filtrez les fichiers par leur nom";
out.or = 'ou';
out.tags_title = "Mots-clés du pad";
out.tags_title = "Mots-clés du pad (pour vous uniquement)";
out.tags_add = "Modifier les mots-clés du pad";
out.tags_searchHint = "Commencez une recherche par # dans votre CryptDrive pour retrouver vos pads par mot-clé.";
out.tags_notShared = "Vos mots-clés ne sont pas partagés avec les autres utilisateurs.";
out.tags_duplicate = "Mot-clé déjà présent : {0}";
out.slideOptionsText = "Options";
@ -265,6 +267,9 @@ define(function () {
out.poll_comment_add = "Ajouter un commentaire";
out.poll_comment_submit = "Envoyer";
out.poll_comment_remove = "Supprimer ce commentaire";
out.poll_comment_placeholder = "Votre commentaire";
out.poll_comment_disabled = "Publiez ce sondage en utilisant le bouton ✓ afin d'activer les commentaires.";
// Canvas
out.canvas_clear = "Nettoyer";
@ -392,6 +397,7 @@ define(function () {
out.fc_remove = "Supprimer définitivement";
out.fc_empty = "Vider la corbeille";
out.fc_prop = "Propriétés";
out.fc_hashtag = "Mots-clés";
out.fc_sizeInKilobytes = "Taille en kilo-octets";
// fileObject.js (logs)
out.fo_moveUnsortedError = "La liste des éléments non triés ne peut pas contenir de dossiers.";
@ -539,6 +545,12 @@ define(function () {
out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'<a href="https://en.wikipedia.org/wiki/Operational_transformation">Operational transformation</a> qui est capable de trouver un consensus distribué en utilisant <a href="https://bitcoin.org/bitcoin.pdf">une chaîne de bloc Nakamoto</a>, un outil popularisé par le <a href="https://fr.wikipedia.org/wiki/Bitcoin">Bitcoin</a>. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.';
//contact.html
out.main_about_p2 = 'Si vous avez des questions ou commentaires, vous pouvez <a href="https://twitter.com/cryptpad"><i class="fa fa-twitter"></i>nous tweeter</a>, ouvrir une issue sur <a href="https://github.com/xwiki-labs/cryptpad/issues/" title="our issue tracker"><i class="fa fa-github"></i>GitHub</a>, venir dire bonjour sur <a href="https://riot.im/app/#/room/#cryptpad:matrix.org" title="Matrix">notre <i class="fa fa-comment"></i>salle Matrix</a> ou IRC (#cryptpad sur irc.freenode.net), ou bien encore <a href="mailto:research@xwiki.com"><i class="fa fa-envelope"></i>nous envoyer un email</a>.';
out.main_about_p22 = 'Tweetez nous';
out.main_about_p23 = 'Ouvrez un ticket (GitHub)';
out.main_about_p24 = 'Dites Bonjour (Matrix)';
out.main_about_p25 = 'Envoyez-nous un email';
out.main_about_p26 = 'Si vous avez une question ou des remarques, n\'hésitez pas à nous contacter !';
out.main_info = "<h2>Collaborez avec confiance</h2><br>Développez vos idées en groupe avec des documents partagés; la technologie <strong>Zero Knowledge</strong> sécurise vos données.";
out.main_catch_phrase = "Le Cloud Zero Knowledge";
@ -584,6 +596,25 @@ define(function () {
out.topbar_whatIsCryptpad = "Qu'est-ce que CryptPad";
// what-is-cryptpad.html
out.whatis_title = "Qu'est-ce que CryptPad";
out.whatis_collaboration = 'Collaboration rapide, facile';
out.whatis_collaboration_p1 = "Avec CryptPad, vous pouvez créer rapidement des documents collaboratifs pour prendre des notes à plusieurs. Quand vous vous enregistrez et vous vous connectez, vous obtenez la possibilité d'importer des fichiers dans un CryptDrive où vous pouvez organiser tous vos pads (documents). En tant qu'utilisateur enregistré, vous possédez 50 Mo de stockage gratuit.";
out.whatis_collaboration_p2 = "Vous pouvez partager l'accès à un document simplement en partageant le lien. Vous pouvez aussi partager un lien spécial fournissant un accès <em>en lecture seule</em> au pad, permettant du publier des travaux collaboratifs tout en restant maître de l'édition.";
out.whatis_collaboration_p3 = "Vous pouvez créer des documents de texte avec <a href=\"http://ckeditor.com/\">CKEditor</a> tout comme des documents Markdown qui sont rendus en temps-réel pendant que vous tapez. Vous pouvez aussi utiliser l'application de sondage pour planifier des évènements avec plusieurs participants.";
out.whatis_zeroknowledge = 'Zero Knowledge';
out.whatis_zeroknowledge_p1 = "Nous ne souhaitons pas connaître ce que vous tapez et grâce à la cryptographie moderne, vous pouvez être assuré que nous ne le <b>pouvons</b> pas. CryptPad utilise <b>un chiffrement à 100% côté client</b> pour protéger le contenu que vous tapez de nous, les personnes contrôlant le serveur.";
out.whatis_zeroknowledge_p2 = "Quand vous vous enregistrez et vous vous connectez, votre nom d'utilisateur et votre mot de passe sont transformés en une clé secrète grâce à la <a href=\"https://fr.wikipedia.org/wiki/Scrypt\">fonction de dérivation de clé Scrypt</a>. Ni cette clé, ni le nom d'utilisateur ou le mot de passe, ne sont envoyés au serveur. À la place, elle est utilisée côté client pour chiffrer et déchiffrer le contenu de votre CryptDrive, qui contient toutes les clés permettant d'accéder à vos pads.";
out.whatis_zeroknowledge_p3 = "Quand vous partagez le lien vers un document, vous partagez la clé cryptographique permettant de déchiffrer le document, mais puisque cette clé se trouve dans l'<a href=\"https://en.wikipedia.org/wiki/Fragment_identifier\">identificateur de fragment</a>, elle n'est jamais envoyée au serveur. Venez lire notre <a href=\"https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/\">article de blog sur la vie privée</a> pour en apprendre davantage sur le type de métadonnées auxquelles nous avons ou n'avons pas accès.";
out.whatis_drive = "Organisation avec CryptDrive";
out.whatis_drive_p1 = "Dés que vous accédez à un pad dans CryptPad, celui-ci est automatiquement ajouté à votre CryptDrive, dans le dossier principal. Vous pouvez alors ranger ce pad dans un dossier ou le déplacer vers la corbeille. CryptDrive vous permet de rechercher parmi vos pads et de les organiser quand vous le souaitez, comme vous le souhaitez.";
out.whatis_drive_p2 = "Avec le glisser-déposer intuitif, vous pouvez déplacer vos pads dans votre drive tout en conservant les liens vers ces pads pour que vos collaborateurs n'en perdent pas l'accès";
out.whatis_drive_p3 = "Vous pouvez également importer des fichier dans votre CryptDrive et les partager avec des collègues. Les fichiers importés peuvent être rangés de la même manière que vos pads collaboratifs.";
out.whatis_business = 'CryptPad for Business';
out.whatis_business_p1 = "Le chiffrement Zero Knowledge de CryptPad excelle pour multiplier l'efficacité des protocoles de sécurité existants en recréant les contrôles d'accès organisationnels de manière cryptographique. Puisque les données sensibles ne peuvent être déchiffrées qu'en utilisant les identifiants d'un employé, CryptPad empêche d'éventuels hackers ayant réussi à s'introduire dans le serveur d'avoir accès en clair à ces données. Découvrez-en plus sur la manière dont CryptPad peut aider votre entreprise en lisant le <a href=\"https://blog.cryptpad.fr/images/CryptPad-Whitepaper-v1.0.pdf\">CryptPad Whitepaper</a>.";
out.whatis_business_p2 = "CryptPad est déployable sur site et les <a href=\"https://cryptpad.fr/about.html\">développeurs CryptPad</a> chez XWiki SAS peuvent effectuer du développement, des personnalisations et du support commercial. Contactez-nous à <a href=\"mailto:sales@cryptpad.fr\">sales@cryptpad.fr</a> pour plus d'informations.";
// privacy.html
out.policy_title = 'Politique de confidentialité de CryptPad';
@ -697,6 +728,7 @@ define(function () {
out.tips.drive = "Les utilisateurs enregistrés peuvent organiser leurs fichiers dans leur CryptDrive, accessible depuis l'icône CryptPad dans le coin supérieur gauche des pads.";
out.tips.profile = "Les utilisateurs enregistrés peuvent créer un profil depuis le menu utilisateur, dans le coin supérieur droit.";
out.tips.avatars = "Vous pouvez uploader un avatar dans votre profil. Les autres personnes le verront dans la liste d'utilisateurs des pads.";
out.tips.tags = "Ajoutez des mots-clés aux pads et effectuer une recherche commençant par # dans votre CryptDrive pour les retrouver.";
out.feedback_about = "Si vous lisez ceci, vous vous demandez probablement pourquoi CryptPad envoie des requêtes vers des pages web quand vous realisez certaines actions.";
out.feedback_privacy = "Nous prenons au sérieux le respect de votre vie privée, et en même temps nous souhaitons rendre CryptPad très simple à utiliser. Nous utilisons cette page pour comprendre quelles fonctionnalités dans l'interface comptent le plus pour les utilisateurs, en l'appelant avec un paramètre spécifiant quelle action a été réalisée.";

@ -158,9 +158,11 @@ define(function () {
out.filePicker_filter = "Filter files by name";
out.or = 'or';
out.tags_title = "Tags";
out.tags_title = "Tags (for you only)";
out.tags_add = "Update this page's tags";
out.tags_searchHint = "Find files by their tags by searching in your CryptDrive";
out.tags_searchHint = "Start a search with # in your CryptDrive to find your tagged pads.";
out.tags_notShared = "Your tags are not shared with other users";
out.tags_duplicate = "Duplicate tag: {0}";
@ -270,6 +272,9 @@ define(function () {
out.poll_comment_add = "Add a comment";
out.poll_comment_submit = "Send";
out.poll_comment_remove = "Delete this comment";
out.poll_comment_placeholder = "Your comment";
out.poll_comment_disabled = "Publish this poll using the ✓ button to enable the comments.";
// Canvas
out.canvas_clear = "Clear";
@ -449,8 +454,6 @@ define(function () {
"<li><i class='fa fa-info-circle'> </i> If you are using a shared computer, you need to log out when you are done, closing the tab is not enough.</li>",
"</ul>"
].join('');
out.register_testimonial =" \"Tools like Etherpad and Google Docs [...] all share a weakness, which is that whomever owns the document server can see everything you're typing. Cryptpad is a free/open project that uses some of the ideas behind blockchain to implement a \"zero-knowledge\" version of a collaborative document editor, ensuring that only the people working on a document can see it.\" ";
out.register_testimonial_name = "Cory Doctorow";
out.register_writtenPassword = "I have written down my username and password, proceed";
out.register_cancel = "Go back";
@ -737,7 +740,7 @@ define(function () {
out.tips.drive = "Logged in users can organize their files in their CryptDrive, accessible from the CryptPad icon at the top left of all pads.";
out.tips.profile = "Registered users can create a profile from the user menu in the top right.";
out.tips.avatars = "You can upload an avatar in your profile. People will see it when you collaborate in a pad.";
out.tips.tags = "You can hashtag pads, and then search by tag in your CryptDrive.";
out.tips.tags = "Tag your pads and start a search with # in your CryptDrive to find them";
out.feedback_about = "If you're reading this, you were probably curious why CryptPad is requesting web pages when you perform certain actions";
out.feedback_privacy = "We care about your privacy, and at the same time we want CryptPad to be very easy to use. We use this file to figure out which UI features matter to our users, by requesting it along with a parameter specifying which action was taken.";

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.16.0",
"version": "1.17.0",
"dependencies": {
"chainpad-server": "^1.0.1",
"express": "~4.10.1",

@ -16,13 +16,17 @@ Installing CryptPad is pretty straightforward. You can read all about it in the
It also contains information on keeping your instance of CryptPad up to date.
## Current version
The most recent version and all past release notes can be found [here](https://github.com/xwiki-labs/cryptpad/releases/).
## Setup using Docker
See [Cryptpad-Docker](docs/cryptpad-docker.md)
See [Cryptpad-Docker](docs/cryptpad-docker.md).
## Setup using Ansible
See [Ansible Role for Cryptpad](https://github.com/systemli/ansible-role-cryptpad)
See [Ansible Role for Cryptpad](https://github.com/systemli/ansible-role-cryptpad).
# Security
@ -75,7 +79,7 @@ If you have any questions or comments, or if you're interested in contributing t
This software is and will always be available under 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. If you wish to use this technology in a proprietary product, please contact
sales@xwiki.com
sales@xwiki.com.
[ChainPad]: https://github.com/xwiki-contrib/chainpad
[active attack]: https://en.wikipedia.org/wiki/Attack_(computing)#Types_of_attacks

@ -76,6 +76,7 @@ define([
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); }
onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href);
});
});
};

@ -179,9 +179,11 @@ define([
var tagger = dialog.frame([
dialog.message([
Messages.tags_add,
h('p', Messages.tags_searchHint)
h('br'),
Messages.tags_searchHint,
]),
input,
h('center', h('small', Messages.tags_notShared)),
dialog.nav(),
]);

@ -489,8 +489,8 @@ define([
};
// STORAGE
common.setPadAttribute = function (attr, value, cb) {
var href = getRelativeHref(window.location.href);
common.setPadAttribute = function (attr, value, cb, href) {
href = getRelativeHref(href || window.location.href);
getStore().setPadAttribute(href, attr, value, cb);
};
common.setDisplayName = function (value, cb) {
@ -1204,8 +1204,8 @@ define([
};
// Forget button
var moveToTrash = common.moveToTrash = function (cb) {
var href = window.location.href;
var moveToTrash = common.moveToTrash = function (cb, href) {
href = href || window.location.href;
common.forgetPad(href, function (err) {
if (err) {
console.log("unable to forget pad");

@ -62,6 +62,46 @@ define([
editor.scrollTo(scroll.left, scroll.top);
};
module.getHeadingText = function (editor) {
var lines = editor.getValue().split(/\n/);
var text = '';
lines.some(function (line) {
// lines including a c-style comment are also valuable
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) {
line.replace(clike, function (a, one, two) {
if (!(two && two.replace)) { return; }
text = two.replace(/\*\/\s*$/, '').trim();
});
return true;
}
// lisps?
var lispy = /^\s*(;|#\|)+(.*?)$/;
if (lispy.test(line)) {
line.replace(lispy, function (a, one, two) {
text = two;
});
return true;
}
// lines beginning with a hash are potentially valuable
// works for markdown, python, bash, etc.
var hash = /^#+(.*?)$/;
if (hash.test(line)) {
line.replace(hash, function (a, one) {
text = one;
});
return true;
}
// TODO make one more pass for multiline comments
});
return text.trim();
};
module.create = function (Common, defaultMode, CMeditor) {
var exp = {};
var Messages = Cryptpad.Messages;
@ -152,43 +192,7 @@ define([
}());
exp.getHeadingText = function () {
var lines = editor.getValue().split(/\n/);
var text = '';
lines.some(function (line) {
// lines including a c-style comment are also valuable
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) {
line.replace(clike, function (a, one, two) {
if (!(two && two.replace)) { return; }
text = two.replace(/\*\/\s*$/, '').trim();
});
return true;
}
// lisps?
var lispy = /^\s*(;|#\|)+(.*?)$/;
if (lispy.test(line)) {
line.replace(lispy, function (a, one, two) {
text = two;
});
return true;
}
// lines beginning with a hash are potentially valuable
// works for markdown, python, bash, etc.
var hash = /^#+(.*?)$/;
if (hash.test(line)) {
line.replace(hash, function (a, one) {
text = one;
});
return true;
}
// TODO make one more pass for multiline comments
});
return text.trim();
return module.getHeadingText(editor);
};
exp.configureLanguage = function (cb, onModeChanged) {

@ -1,8 +1,9 @@
define([
'jquery',
'/file/file-crypto.js',
'/common/common-thumbnail.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto) {
], function ($, FileCrypto, Thumb) {
var Nacl = window.nacl;
var module = {};
@ -220,30 +221,46 @@ define([
var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb;
var finish = function (arrayBuffer) {
var file_arraybuffer;
var finish = function () {
var metadata = {
name: file.name,
type: file.type,
};
if (thumb) { metadata.thumbnail = thumb; }
queue.push({
blob: arrayBuffer,
blob: file_arraybuffer,
metadata: metadata,
dropEvent: e
});
};
var processFile = function () {
blobToArrayBuffer(file, function (e, buffer) {
finish(buffer);
});
};
if (!thumbnail) { return void processFile(); }
blobToArrayBuffer(thumbnail, function (e, buffer) {
blobToArrayBuffer(file, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
processFile();
file_arraybuffer = buffer;
if (thumbnail) { // there is already a thumbnail
return blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
finish();
});
}
if (!Thumb.isSupportedType(file.type)) { return finish(); }
// make a resized thumbnail from the image..
Thumb.fromImageBlob(file, function (e, thumb_blob) {
if (e) { console.error(e); }
if (!thumb_blob) { return finish(); }
blobToArrayBuffer(thumb_blob, function (e, buffer) {
if (e) {
console.error(e);
return finish();
}
thumb = arrayBufferToString(buffer);
finish();
});
});
});
};

@ -150,9 +150,6 @@ define([
'class': "fa fa-trash cryptpad-forget",
style: 'font:'+size+' FontAwesome'
});
if (!common.isStrongestStored()) {
button.addClass('cp-toolbar-hidden');
}
if (callback) {
button
.click(common.prepareFeedback(type))
@ -216,7 +213,7 @@ define([
title: Messages.tags_title,
})
.click(common.prepareFeedback(type))
.click(function () { UI.updateTags(null); });
.click(function () { UI.updateTags(common, null); });
break;
default:
button = $('<button>', {

@ -216,6 +216,12 @@ define([
});
sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) {
cb = cb || $.noop;
if (readOnly && hashes.editHash) {
var appPath = window.location.pathname;
Cryptpad.moveToTrash(cb, appPath + '#' + hashes.editHash);
return;
}
Cryptpad.moveToTrash(cb);
});
@ -262,7 +268,7 @@ define([
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
var decryptedMsg = crypto.decrypt(msg, true);
msgs.push(decryptedMsg);
}
};

@ -657,9 +657,10 @@ define([
// We need to override the "a" tag action here because it is inside the iframe!
var inDrive = /^\/drive/;
var origin = config.metadataMgr.getPrivateData().origin;
var href = inDrive.test(origin) ? origin+'/index.html' : origin+'/drive/';
var privateData = config.metadataMgr.getPrivateData();
var origin = privateData.origin;
var pathname = privateData.pathname;
var href = inDrive.test(pathname) ? origin+'/index.html' : origin+'/drive/';
var buttonTitle = inDrive ? Messages.header_homeTitle : Messages.header_logoTitle;
var $aTag = $('<a>', {

@ -6,6 +6,7 @@
<style>
.loading-hidden { display: none; }
#editor1 { display: none; }
.cp-app-drive-context { display: none; }
</style>
</head>
<body class="cp-app-drive">

@ -77,7 +77,7 @@ define([
sfCommon: common,
};
if (uploadMode) {
displayed.push('pageTitle'); //TODO in toolbar
displayed.push('pageTitle');
configTb.pageTitle = Messages.upload_title;
}
var toolbar = APP.toolbar = Toolbar.create(configTb);

@ -28,7 +28,7 @@
@poll-uncommitted-bg: #ddd; //lighten(@poll-th-bg, 50%);
@poll-uncommitted-text: black;
@poll-placeholder: #666;
@poll-placeholder: #fff;
@poll-border-color: #555;
@poll-cover-color: #000;
@poll-fg: #000;
@ -63,10 +63,6 @@ overflow-x: hidden;
}
}
.cp-app-poll-table-text-cell input[type="text"] {
width: 400px;
}
input[type="text"], textarea {
background-color: white;
color: black;
@ -81,12 +77,13 @@ input[type="text"][disabled], textarea[disabled] {
// The placeholder color only seems to effect Safari when not set
input[type="text"][disabled]::placeholder {
//color: @poll-placeholder;
color: @poll-placeholder;
opacity: 1;
}
table#cp-app-poll-table {
margin: 0px;
overflow: hidden;
}
#cp-app-poll-table-container {
position: relative;
@ -106,6 +103,7 @@ table#cp-app-poll-table {
overflow: hidden;
}
#cp-app-poll-create-option {
order: 3;
display: inline-flex;
width: 50px;
height: 24px;
@ -125,6 +123,7 @@ table#cp-app-poll-table {
min-width: 80%;
width: 80%;
min-height: 200px;
height: 200px;
border: 1px solid black;
.CodeMirror-placeholder {
color: #777;
@ -149,18 +148,26 @@ table#cp-app-poll-table {
max-height: 20em;
}
}
.cp-app-poll-published {
#cp-app-poll-description {
display: none;
&~ .CodeMirror {
div.cp-app-poll-published {
div.cp-app-poll-realtime {
#cp-app-poll-description {
display: none;
&~ .CodeMirror {
display: none;
}
}
}
#cp-app-poll-description-published {
display: block;
&:empty {
#cp-app-poll-description-published {
display: block;
&:empty {
display: none;
}
}
#cp-app-poll-nocomments {
display: none;
}
#cp-app-poll-comments {
display: block;
}
}
}
@ -218,6 +225,15 @@ div.cp-app-poll-realtime {
padding: 0px;
margin: 0px;
.cp-app-poll-table-scrolled {
tr td:last-child {
right: 0;
}
tr td:nth-last-child(2) {
right: 100px;
}
}
table {
border-collapse: collapse;
width: ~"calc(100% - 1px)";
@ -231,10 +247,6 @@ div.cp-app-poll-realtime {
div.cp-app-poll-table-text-cell {
background-color: @poll-uncommitted-bg !important;
color: @poll-uncommitted-text !important;
input {
width: ~"calc(100% - 80px)" !important;
vertical-align: middle;
}
}
text-align: center;
background-color: @poll-uncommitted-bg !important;
@ -272,14 +284,22 @@ div.cp-app-poll-realtime {
margin: 0px;
div.cp-app-poll-table-text-cell {
height: 28px;
padding: 0px;
margin: 0px;
height: 100%;
display: flex;
align-items: center;
.cp-app-poll-table-remove {
order: 1;
}
.cp-app-poll-table-edit {
order: 3;
}
input {
width: 80%;
width: 90%;
height: 100%;
min-width: 0;
order: 2;
flex: 1;
height: 24px;
border: 0px;
margin: 2px;
&[disabled] {
@ -375,6 +395,9 @@ div.cp-app-poll-realtime {
}
thead {
height: 52px;
tr {
height: 52px;
}
td {
padding: 0px 5px;
background: @poll-th-bg;
@ -382,6 +405,11 @@ div.cp-app-poll-realtime {
&:not(:last-child) {
border-right: 1px solid rgba(255,255,255,0.2);
}
&:last-child {
height: 52px;
line-height: 52px;
text-align: center;
}
&:nth-last-child(2) {
border-right: 1px solid @poll-border-color;
}
@ -490,11 +518,18 @@ div.cp-app-poll-realtime {
display: none;
}
}
#cp-app-poll-nocomments {
color: #999;
text-align: center;
margin: 20px;
font: @colortheme_app-font;
}
#cp-app-poll-comments {
width: 50%;
margin: 20px auto;
min-width: 400px;
padding-bottom: 5px;
display: none;
button {
border-radius: 0;
}

@ -46,9 +46,7 @@ define([
var Messages = Cryptpad.Messages;
var saveAs = window.saveAs;
var Render = Renderer(Cryptpad);
var APP = window.APP = {
Render: Render,
unlocked: {
row: [],
col: []
@ -57,6 +55,7 @@ define([
Cryptpad: Cryptpad,
mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles
};
var Render = Renderer(Cryptpad, APP);
var debug = $.noop; //console.log;
@ -221,11 +220,12 @@ define([
return newObj;
};
var enableColumn = function (id) {
var $input = $('input[disabled="disabled"][data-rt-id^="' + id + '"]')
var enableColumn = APP.enableColumn = function (id, table) {
table = table || $('body');
var $input = $(table).find('input[disabled="disabled"][data-rt-id^="' + id + '"]')
.removeAttr('disabled');
$input.closest('td').addClass('cp-app-poll-table-editing');
$('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').addClass('fa-unlock')
$(table).find('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').addClass('fa-unlock')
.removeClass('fa-lock').attr('title', Messages.poll_unlocked);
};
var disableColumn = function (id) {
@ -235,10 +235,13 @@ define([
$('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').addClass('fa-lock')
.removeClass('fa-unlock').attr('title', Messages.poll_locked);
};
var enableRow = function (id) {
var $input = $('input[type="text"][disabled="disabled"][data-rt-id="' + id + '"]').removeAttr('disabled');
var enableRow = APP.enableRow = function (id, table) {
table = table || $('body');
var $input = $(table).find('input[disabled="disabled"][data-rt-id="' + id + '"]')
.removeAttr('disabled');
$input.closest('td').addClass('cp-app-poll-table-editing');
$('span.cp-app-poll-table-edit[data-rt-id="' + id + '"]').css('visibility', 'hidden');
$(table).find('span.cp-app-poll-table-edit[data-rt-id="' + id + '"]')
.css('visibility', 'hidden');
};
var disableRow = function (id) {
var $input = $('input[type="text"][data-rt-id="' + id + '"]')
@ -247,77 +250,11 @@ define([
$('span.cp-app-poll-table-edit[data-rt-id="' + id + '"]').css('visibility', 'visible');
};
var styleUserColumn = function () {
var userid = APP.userid;
if (!userid) { return; }
// Enable input for the userid column
enableColumn(userid);
$('input[disabled="disabled"][data-rt-id^="' + userid + '"]')
.attr('placeholder', Messages.poll_userPlaceholder);
$('.cp-app-poll-table-lock[data-rt-id="' + userid + '"]').remove();
$('[data-rt-id^="' + userid + '"]').closest('td')
.addClass("cp-app-poll-table-own");
$('.cp-app-poll-table-bookmark[data-rt-id="' + userid + '"]').css('visibility', '')
.addClass('cp-app-poll-table-bookmark-full')
.attr('title', Messages.poll_bookmarked_col);
};
var styleUncommittedColumn = function () {
var $scroll = $('#cp-app-poll-table-scroll');
var hasScroll = $scroll.width() < $scroll[0].scrollWidth;
APP.uncommitted.content.colsOrder.forEach(function(id) {
// Enable the checkboxes for the uncommitted column
enableColumn(id);
$('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').remove();
$('.cp-app-poll-table-remove[data-rt-id="' + id + '"]').remove();
$('.cp-app-poll-table-bookmark[data-rt-id="' + id + '"]').remove();
$('td.cp-app-poll-table-uncommitted .cover').addClass("cp-app-poll-table-uncommitted");
var $uncommittedCol = $('[data-rt-id^="' + id + '"]').closest('td');
$uncommittedCol.addClass("cp-app-poll-table-uncommitted");
if (hasScroll) {
$uncommittedCol.css('right', '100px');
}
});
APP.uncommitted.content.rowsOrder.forEach(function(id) {
// Enable the checkboxes for the uncommitted column
enableRow(id);
$('.cp-app-poll-table-edit[data-rt-id="' + id + '"]').remove();
$('.cp-app-poll-table-remove[data-rt-id="' + id + '"]').remove();
$('[data-rt-id="' + id + '"]').closest('tr').addClass("cp-app-poll-table-uncommitted");
});
};
var unlockElements = function () {
APP.unlocked.row.forEach(enableRow);
APP.unlocked.col.forEach(enableColumn);
};
var updateTableButtons = function () {
var uncomColId = APP.uncommitted.content.colsOrder[0];
var uncomRowId = APP.uncommitted.content.rowsOrder[0];
var $createOption = APP.$table.find('tbody input[data-rt-id="' + uncomRowId+'"]')
.closest('td').find('> div');
$createOption.append(APP.$createRow);
var $createUser = APP.$table.find('thead input[data-rt-id="' + uncomColId + '"]')
.closest('td');
$createUser.prepend(APP.$createCol);
if (APP.proxy.content.colsOrder.indexOf(APP.userid) === -1) {
APP.$table.find('.cp-app-poll-table-bookmark').css('visibility', '');
}
//$('#cp-app-poll-create-user, #cp-app-poll-create-option').css('display', 'inline-flex');
if (!APP.proxy ||
!APP.proxy.content.rowsOrder ||
APP.proxy.content.rowsOrder.length === 0) {
//$('#create-user').hide();
}
var width = $('#cp-app-poll-table').outerWidth();
if (width) {
//$('#create-user').css('left', width + 30 + 'px');
}
};
var setTablePublished = function (bool) {
if (bool) {
if (APP.$publish) { APP.$publish.hide(); }
@ -329,59 +266,32 @@ define([
$('#cp-app-poll-form').removeClass('cp-app-poll-published');
}
};
var addCount = function () {
var addScrollClass = function () {
var $scroll = $('#cp-app-poll-table-scroll');
var hasScroll = $scroll.width() < $scroll[0].scrollWidth;
var $countCol = $('tr td:last-child');
var hasScroll = $scroll.width() < $scroll[0].scrollWidth && $scroll.width() > 100;
if (hasScroll) {
$countCol.css('right', '0');
$scroll.addClass('cp-app-poll-table-scrolled');
return;
}
var $thead = APP.$table.find('thead');
var $tr = APP.$table.find('tbody tr').first();
$thead.find('tr td').last()
.css({
'height': $thead.height()+'px',
'text-align': 'center',
'line-height': $thead.height()+'px'
})
.text(Messages.poll_total);
var winner = {
v: 0,
ids: []
};
APP.count = {};
APP.proxy.content.rowsOrder.forEach(function (rId) {
var count = Object.keys(APP.proxy.content.cells)
.filter(function (k) {
return k.indexOf(rId) !== -1 && APP.proxy.content.cells[k] === 1;
}).length;
if (count > winner.v) {
winner.v = count;
winner.ids = [rId];
} else if (count && count === winner.v) {
winner.ids.push(rId);
}
APP.count[rId] = count;
APP.$table.find('[data-rt-count-id="' + rId + '"]')
.text(count)
.css({
'height': $tr.height()+'px',
'line-height': $tr.height()+'px'
});
});
winner.ids.forEach(function (rId) {
$('[data-rt-id="' + rId + '"]').closest('td').addClass('cp-app-poll-table-winner');
$('[data-rt-count-id="' + rId + '"]').addClass('cp-app-poll-table-winner');
});
$scroll.removeClass('cp-app-poll-table-scrolled');
};
var updateTableButtons = function () {
var uncomColId = APP.uncommitted.content.colsOrder[0];
var uncomRowId = APP.uncommitted.content.rowsOrder[0];
var $createOption = $('tbody input[data-rt-id="' + uncomRowId+'"]')
.closest('td').find('> div');
$createOption.find('#cp-app-poll-create-option').remove();
$createOption.append(APP.$createRow);
var $createUser = $('thead input[data-rt-id="' + uncomColId + '"]')
.closest('td');
$createUser.find('#cp-app-poll-create-user').remove();
$createUser.prepend(APP.$createCol);
};
var updateDisplayedTable = function () {
styleUserColumn();
styleUncommittedColumn();
unlockElements();
updateTableButtons();
setTablePublished(APP.proxy.published);
addCount();
addScrollClass();
updateTableButtons();
};
var unlockColumn = function (id, cb) {
@ -464,11 +374,13 @@ define([
Render.updateTable(table, displayedObj, conf);
// Fix autocomplete bug:
displayedObj.content.rowsOrder.forEach(function (rowId) {
if (f.id === rowId) { return; }
$('input[data-rt-id="' + rowId +'"]').val(displayedObj.content.rows[rowId] || '');
});
displayedObj.content.colsOrder.forEach(function (rowId) {
$('input[data-rt-id="' + rowId +'"]')
.val(displayedObj.content.cols[rowId] || '');
displayedObj.content.colsOrder.forEach(function (colId) {
if (f.id === colId) { return; }
$('input[data-rt-id="' + colId +'"]')
.val(displayedObj.content.cols[colId] || '');
});
updateDisplayedTable();
setFocus(f);
@ -512,7 +424,7 @@ define([
case 'text':
debug("text[rt-id='%s'] [%s]", id, input.value);
Render.setValue(object, id, input.value);
change(null, null, null, 250);
change(null, null, null, 1000);
break;
case 'number':
debug("checkbox[tr-id='%s'] %s", id, input.value);
@ -630,6 +542,7 @@ define([
switch (nodeName) {
case 'INPUT':
if ($(target).is('[type="text"]') && !isKeyup) { return; }
if (isKeyup && (e.keyCode === 13 || e.keyCode === 27)) {
var id = target.getAttribute('data-rt-id');
if ($(target).parents('.cp-app-poll-table-uncommitted').length
@ -783,6 +696,7 @@ define([
return comments[a].time > comments[b].time;
}).forEach(function (k) {
var c = comments[k];
var name = c.name || Messages.anonymous;
var $c = $('<div>', {
'class': 'cp-app-poll-comments-list-el'
}).prependTo($comments);
@ -794,7 +708,7 @@ define([
if (c.avatar && avatars[c.avatar]) {
$avatar.append(avatars[c.avatar]);
} else {
common.displayAvatar($avatar, c.avatar, c.name, function ($img) {
common.displayAvatar($avatar, c.avatar, name, function ($img) {
if (c.avatar && $img.length) { avatars[c.avatar] = $img[0].outerHTML; }
});
}
@ -803,11 +717,11 @@ define([
'href': APP.origin + '/profile/#' + c.profile,
'target': '_blank',
'class': 'cp-app-poll-comments-list-data-name'
}).appendTo($data).text(c.name);
}).appendTo($data).text(name);
} else {
$('<span>', {
'class': 'cp-app-poll-comments-list-data-name'
}).appendTo($data).text(c.name);
}).appendTo($data).text(name);
}
$('<span>', {
'class': 'cp-app-poll-comments-list-data-time'
@ -845,10 +759,12 @@ define([
};
var addComment = function () {
if (!APP.proxy.comments) { APP.proxy.comments = {}; }
var name = APP.$addComment.find('.cp-app-poll-comments-add-name').val();
var msg = APP.$addComment.find('.cp-app-poll-comments-add-msg').val();
var name = APP.$addComment.find('.cp-app-poll-comments-add-name').val().trim();
var msg = APP.$addComment.find('.cp-app-poll-comments-add-msg').val().trim();
var time = +new Date();
if (!msg) { return; }
var profile, avatar;
if (common.isLoggedIn()) {
profile = metadataMgr.getUserData().profile;
@ -938,12 +854,6 @@ define([
unlockRow(rowuid);
}
var displayedObj = mergeUncommitted(proxy, uncommitted, false);
var colsOrder = sortColumns(displayedObj.content.colsOrder, userid);
var $table = APP.$table = $(Render.asHTML(displayedObj, null, colsOrder, APP.readOnly));
/*
Extract uncommitted data (row or column) and create a new uncommitted row or column
*/
@ -1004,6 +914,15 @@ define([
});
});
var displayedObj = mergeUncommitted(proxy, uncommitted, false);
var colsOrder = sortColumns(displayedObj.content.colsOrder, userid);
Render.updateTable($('#cp-app-poll-table-scroll').find('table')[0], displayedObj, {
cols: colsOrder,
readOnly: APP.readOnly
});
// Description
APP.editor.on('change', function () {
var val = APP.editor.getValue();
@ -1012,7 +931,7 @@ define([
APP.$addComment.find('.cp-app-poll-comments-add-submit').click(addComment);
APP.$addComment.find('.cp-app-poll-comments-add-cancel').click(resetComment);
$('#cp-app-poll-table-scroll').html('').prepend($table);
var $table = APP.$table = $('#cp-app-poll-table-scroll').find('table');
updateDisplayedTable();
updateDescription(null, APP.proxy.description || '');
@ -1079,6 +998,11 @@ define([
Cryptpad.findOKButton().click();
};
var getHeadingText = function () {
if (!APP.editor) { return; }
return SframeCM.getHeadingText(APP.editor);
};
var onCreate = function (info) {
APP.myID = info.myID;
@ -1092,7 +1016,8 @@ define([
metadataMgr = common.getMetadataMgr();
Title = common.createTitle();
var titleCfg = { getHeadingText: getHeadingText };
Title = common.createTitle(titleCfg);
var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'share', 'userlist', 'newpad', 'limit'],

@ -1,9 +1,10 @@
define([
//'/common/cryptpad-common.js',
'jquery',
'/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.js',
'/bower_components/diff-dom/diffDOM.js',
], function (Hyperjson, TextPatcher) {
], function ($, Hyperjson, TextPatcher) {
var DiffDOM = window.diffDOM;
var Example = {
@ -30,7 +31,7 @@ by maintaining indexes in rowsOrder and colsOrder
}
};
var Renderer = function (Cryptpad) {
var Renderer = function (Cryptpad, APP) {
var Render = {
Example: Example
@ -222,7 +223,9 @@ var Renderer = function (Cryptpad) {
disabled: 'disabled'
};
return result;
})).concat([null]);
})).concat([{
content: Cryptpad.Messages.poll_total
}]);
}
if (i === rows.length) {
return [null].concat(cols.map(function () {
@ -307,7 +310,7 @@ var Renderer = function (Cryptpad) {
}
return ['TD', {}, elements];
}
return ['TD', cell, []];
return ['TD', cell, [cell.content]];
};
var clone = function (o) {
@ -444,6 +447,107 @@ var Renderer = function (Cryptpad) {
}
};
var styleUserColumn = function (table) {
var userid = APP.userid;
if (!userid) { return; }
// Enable input for the userid column
APP.enableColumn(userid, table);
$(table).find('input[disabled="disabled"][data-rt-id^="' + userid + '"]')
.attr('placeholder', Cryptpad.Messages.poll_userPlaceholder);
$(table).find('.cp-app-poll-table-lock[data-rt-id="' + userid + '"]').remove();
$(table).find('[data-rt-id^="' + userid + '"]').closest('td')
.addClass("cp-app-poll-table-own");
$(table).find('.cp-app-poll-table-bookmark[data-rt-id="' + userid + '"]')
.css('visibility', '')
.addClass('cp-app-poll-table-bookmark-full')
.attr('title', Cryptpad.Messages.poll_bookmarked_col);
};
var styleUncommittedColumn = function (table) {
APP.uncommitted.content.colsOrder.forEach(function(id) {
// Enable the checkboxes for the uncommitted column
APP.enableColumn(id, table);
$(table).find('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').remove();
$(table).find('.cp-app-poll-table-remove[data-rt-id="' + id + '"]').remove();
$(table).find('.cp-app-poll-table-bookmark[data-rt-id="' + id + '"]').remove();
$(table).find('td.cp-app-poll-table-uncommitted .cover')
.addClass("cp-app-poll-table-uncommitted");
var $uncommittedCol = $(table).find('[data-rt-id^="' + id + '"]').closest('td');
$uncommittedCol.addClass("cp-app-poll-table-uncommitted");
});
APP.uncommitted.content.rowsOrder.forEach(function(id) {
// Enable the checkboxes for the uncommitted column
APP.enableRow(id, table);
$(table).find('.cp-app-poll-table-edit[data-rt-id="' + id + '"]').remove();
$(table).find('.cp-app-poll-table-remove[data-rt-id="' + id + '"]').remove();
$(table).find('[data-rt-id="' + id + '"]').closest('tr')
.addClass("cp-app-poll-table-uncommitted");
});
};
var unlockElements = function (table) {
APP.unlocked.row.forEach(function (id) { APP.enableRow(id, table); });
APP.unlocked.col.forEach(function (id) { APP.enableColumn(id, table); });
};
var updateTableButtons = function (table) {
var uncomColId = APP.uncommitted.content.colsOrder[0];
var uncomRowId = APP.uncommitted.content.rowsOrder[0];
var $createOption = $(table).find('tbody input[data-rt-id="' + uncomRowId+'"]')
.closest('td').find('> div');
$createOption.append(APP.$createRow);
var $createUser = $(table).find('thead input[data-rt-id="' + uncomColId + '"]')
.closest('td');
$createUser.prepend(APP.$createCol);
if (APP.proxy.content.colsOrder.indexOf(APP.userid) === -1) {
$(table).find('.cp-app-poll-table-bookmark').css('visibility', '');
}
};
var addCount = function (table) {
var $tr = $(table).find('tbody tr').first();
var winner = {
v: 0,
ids: []
};
APP.count = {};
APP.proxy.content.rowsOrder.forEach(function (rId) {
var count = Object.keys(APP.proxy.content.cells)
.filter(function (k) {
return k.indexOf(rId) !== -1 && APP.proxy.content.cells[k] === 1;
}).length;
if (count > winner.v) {
winner.v = count;
winner.ids = [rId];
} else if (count && count === winner.v) {
winner.ids.push(rId);
}
APP.count[rId] = count;
var h = $tr.height() || 28;
$(table).find('[data-rt-count-id="' + rId + '"]')
.text(count)
.css({
'height': h+'px',
'line-height': h+'px'
});
});
winner.ids.forEach(function (rId) {
$(table).find('[data-rt-id="' + rId + '"]').closest('td')
.addClass('cp-app-poll-table-winner');
$(table).find('[data-rt-count-id="' + rId + '"]')
.addClass('cp-app-poll-table-winner');
});
};
var styleTable = function (table) {
styleUserColumn(table);
styleUncommittedColumn(table);
unlockElements(table);
updateTableButtons(table);
addCount(table);
};
Render.updateTable = function (table, obj, conf) {
var DD = new DiffDOM(diffOptions);
@ -457,6 +561,9 @@ var Renderer = function (Cryptpad) {
if (!hj) { throw new Error("Expected Hyperjson!"); }
var table2 = Hyperjson.toDOM(hj);
styleTable(table2);
var patch = DD.diff(table, table2);
DD.apply(table, patch);
};

@ -77,7 +77,7 @@ define([
var c = Slide.content;
var m = '<span class="cp-app-slide-container"><span class="'+slideClass+'">'+DiffMd.render(c).replace(separatorReg, '</span></span><span class="cp-app-slide-container"><span class="'+slideClass+'">')+'</span></span>';
DiffMd.apply(m, $content);
try { DiffMd.apply(m, $content); } catch (e) { return console.error(e); }
var length = getNumberOfSlides();
$modal.find('style.cp-app-slide-style').remove();

@ -46,6 +46,7 @@
// contains user tools
#cp-app-whiteboard-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;

Loading…
Cancel
Save