diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 0e42b9a3a..c37e4b2f5 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -62,7 +62,7 @@ define([ var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ? '/imprint.html' : AppConfig.imprint); - Pages.versionString = "CryptPad v3.19.0 (Thylacine)"; + Pages.versionString = "CryptPad v3.19.1 (Thylacine's revenge)"; // used for the about menu Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined; diff --git a/customize.dist/src/less2/pages/page-register.less b/customize.dist/src/less2/pages/page-register.less index 314ad7236..cc319a0e4 100644 --- a/customize.dist/src/less2/pages/page-register.less +++ b/customize.dist/src/less2/pages/page-register.less @@ -2,9 +2,11 @@ @import (reference) "../include/colortheme-all.less"; @import (reference) "../include/alertify.less"; @import (reference) "../include/checkmark.less"; +@import (reference) "../include/forms.less"; &.cp-page-register { .infopages_main(); + .forms_main(); .alertify_main(); .checkmark_main(20px); diff --git a/package-lock.json b/package-lock.json index ed2097eff..dcced0ee4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "3.19.0", + "version": "3.19.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index be6f77978..8b9bc912c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "3.19.0", + "version": "3.19.1", "license": "AGPL-3.0+", "repository": { "type": "git", diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 601375b96..d406eccaf 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -647,9 +647,6 @@ define([ var $ok = $(ok).click(function (ev) { close(true, ev); }); var $cancel = $(cancel).click(function (ev) { close(false, ev); }); - if (opt.cancelClass) { $cancel.addClass(opt.cancelClass); } - if (opt.okClass) { $ok.addClass(opt.okClass); } - listener = listenForKeys(function () { $ok.click(); }, function () { diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 265a61639..fc514ef3b 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2155,6 +2155,11 @@ define([ 'class': 'fa fa-caret-down', }).prependTo($button); } + if (config.angleDown) { + $('', { + 'class': 'fa fa-angle-down', + }).prependTo($button); + } // Menu var $innerblock = $('
', {'class': 'cp-dropdown-content'}); diff --git a/www/common/migrate-user-object.js b/www/common/migrate-user-object.js index ef73e5f3a..000744cc9 100644 --- a/www/common/migrate-user-object.js +++ b/www/common/migrate-user-object.js @@ -500,7 +500,7 @@ define([ }, 500); progress(0, 0);*/ }).nThen(function () { - Realtime.whenRealtimeSyncs(store.realtime, Util.bake(cb)); + Realtime.whenRealtimeSyncs(store.realtime, Util.mkAsync(Util.bake(cb))); }); }; }); diff --git a/www/common/outer/roster.js b/www/common/outer/roster.js index d7976c8da..cd73c074c 100644 --- a/www/common/outer/roster.js +++ b/www/common/outer/roster.js @@ -798,7 +798,6 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { return void console.error(err); } metadata = ref.internal.metadata = (data && data[0]) || undefined; - console.log("TEAM_METADATA", metadata); }); }).nThen(function (w) { if (!config.keys.teamEdPublic && metadata && metadata.validateKey) { diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 5c31621f7..242802bc5 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -175,6 +175,23 @@ define([ Pinpad.create(ctx.store.network, data, function (e, call) { if (e) { return void cb(e); } team.rpc = call; + team.pin = function (data, cb) { + if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); } + if (typeof(cb) !== 'function') { console.error('expected a callback'); } + team.rpc.pin(data, function (e, hash) { + if (e) { return void cb({error: e}); } + cb({hash: hash}); + }); + }; + + team.unpin = function (data, cb) { + if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); } + if (typeof(cb) !== 'function') { console.error('expected a callback'); } + team.rpc.unpin(data, function (e, hash) { + if (e) { return void cb({error: e}); } + cb({hash: hash}); + }); + }; cb(); }); }); @@ -245,27 +262,7 @@ define([ nThen(function (waitFor) { // Init Team RPC if (!keys.drive.edPrivate) { return; } - initRpc(ctx, team, keys.drive, waitFor(function (err) { - if (err) { return; } - - team.pin = function (data, cb) { - if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); } - if (typeof(cb) !== 'function') { console.error('expected a callback'); } - team.rpc.pin(data, function (e, hash) { - if (e) { return void cb({error: e}); } - cb({hash: hash}); - }); - }; - - team.unpin = function (data, cb) { - if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); } - if (typeof(cb) !== 'function') { console.error('expected a callback'); } - team.rpc.unpin(data, function (e, hash) { - if (e) { return void cb({error: e}); } - cb({hash: hash}); - }); - }; - })); + initRpc(ctx, team, keys.drive, waitFor(function () {})); }).nThen(function () { // Create the proxy manager var loadSharedFolder = function (id, data, cb, isNew) { @@ -474,6 +471,16 @@ define([ // Make sure we have not been kicked from the roster var state = roster.getState(); var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']); + // XXX FIXME roster history temporarily corrupted, don't leave the team + if (!state.members || !Object.keys(state.members).length) { + lm.stop(); + roster.stop(); + lm.proxy = {}; + cb({error: 'EINVAL'}); + waitFor.abort(); + console.error(JSON.stringify(state)); + return; + } if (!state.members[me]) { lm.stop(); roster.stop(); @@ -1122,6 +1129,7 @@ define([ teamData.hash = data.hash; teamData.keys.drive.edPrivate = data.keys.drive.edPrivate; teamData.keys.chat.edit = data.keys.chat.edit; + initRpc(ctx, team, teamData.keys.drive, function () {}); var secret = Hash.getSecrets('team', data.hash, teamData.password); team.secondaryKey = secret && secret.keys.secondaryKey; @@ -1130,6 +1138,9 @@ define([ delete teamData.keys.drive.edPrivate; delete teamData.keys.chat.edit; delete team.secondaryKey; + if (team.rpc && team.rpc.destroy) { + team.rpc.destroy(); + } } updateMyRights(ctx, teamId, data.hash); @@ -1660,6 +1671,7 @@ define([ var safe = false; if (['drive', 'teams', 'settings'].indexOf(app) !== -1) { safe = true; } Object.keys(teams).forEach(function (id) { + if (!ctx.teams[id]) { return; } t[id] = { owner: teams[id].owner, name: teams[id].metadata.name, @@ -1716,6 +1728,15 @@ define([ team.removeClient = function (clientId) { removeClient(ctx, clientId); }; + var listTeams = function (cb) { + var t = Util.clone(teams); + Object.keys(t).forEach(function (id) { + // If failure to load the team, don't send it + if (ctx.teams[id]) { return; } + t[id].error = true; + }); + cb(t); + }; team.execCommand = function (clientId, obj, cb) { if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); @@ -1728,7 +1749,7 @@ define([ return void subscribe(ctx, data, clientId, cb); } if (cmd === 'LIST_TEAMS') { - return void cb(store.proxy.teams); + return void listTeams(cb); } if (cmd === 'OPEN_TEAM_CHAT') { return void openTeamChat(ctx, data, clientId, cb); diff --git a/www/common/sframe-common-mailbox.js b/www/common/sframe-common-mailbox.js index bdf1ae81e..c89bdff3c 100644 --- a/www/common/sframe-common-mailbox.js +++ b/www/common/sframe-common-mailbox.js @@ -144,7 +144,6 @@ define([ var onMessage = function (data, cb) { // data = { type: 'type', content: {msg: 'msg', hash: 'hash'} } - console.debug(data.type, data.content); pushMessage(data); if (data.content && typeof (data.content.getFormatText) === "function") { var text = $('
').html(data.content.getFormatText()).text(); @@ -188,7 +187,6 @@ define([ onMessageHandlers.push(function (data, el) { var type = data.type; if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; } - console.log('okokok'); cfg.onMessage(data, el); }); } diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 3afa55d61..e55cdcf82 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -1027,7 +1027,6 @@ MessengerUI, Messages) { Common.mailbox.subscribe(['notifications', 'team'], { onMessage: function (data, el) { - console.log(data, el, div); if (el) { $(div).prepend(el); } diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index d40c9e552..3ab3680ea 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1087,7 +1087,7 @@ "support_disabledTitle": "Support ist nicht aktiviert", "support_disabledHint": "Diese CryptPad-Instanz wurde noch nicht für die Verwendung eines Support-Formulars konfiguriert.", "support_cat_new": "Neues Ticket", - "support_formTitle": "Titel des Tickets", + "support_formTitle": "Neues Ticket", "support_formHint": "Mit diesem Formular kann ein neues Support-Ticket eröffnet werden. Es erlaubt die sichere Kontaktaufnahme mit den Administratoren zur Lösung von Problemen oder Beantwortung von Fragen. Bitte eröffne kein neues Ticket, wenn du bereits ein offenes Ticket bezüglich des gleichen Problems hast. Verwende stattdessen die Antworten-Schaltfläche, um weitere Informationen hinzuzufügen.", "support_formButton": "Absenden", "support_formTitleError": "Fehler: Titel ist leer", diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index ef33ad144..f4272d26a 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1086,7 +1086,7 @@ "support_disabledTitle": "Le support n'est pas activé", "support_disabledHint": "Cette instance de CryptPad n'est pas encore configurée pour utiliser le formulaire de support.", "support_cat_new": "Nouveau ticket", - "support_formTitle": "Titre du ticket", + "support_formTitle": "Nouveau Ticket", "support_formButton": "Envoyer", "support_formTitleError": "Erreur : le titre est vide", "support_formContentError": "Erreur : le contenu est vide", diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 2aee6cc19..d99fd8d18 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1108,7 +1108,7 @@ "support_disabledTitle": "Support is not enabled", "support_disabledHint": "This CryptPad instance is not yet configured to use a support form.", "support_cat_new": "New ticket", - "support_formTitle": "Ticket title", + "support_formTitle": "New Ticket", "support_formHint": "This form can be used to create a new support ticket. Use it to contact the administrators to solve issues or ask any question in a secure way. Please don't create a new ticket if you already have an open ticket about the same issue, but use the reply button to provide more information.", "support_formButton": "Send", "support_formTitleError": "Error: title is empty", diff --git a/www/register/main.js b/www/register/main.js index d052f88b0..e2c30ea64 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -115,8 +115,11 @@ define([ }, { ok: Messages.register_writtenPassword, cancel: Messages.register_cancel, - cancelClass: 'safe', - okClass: 'danger', +/* If we're certain that we aren't using these "*Class" APIs + anywhere else then we can deprecate them and make this a + custom modal in common-interface (or here). */ + cancelClass: 'btn.btn-safe', + okClass: 'btn.btn-danger', reverseOrder: true, done: function ($dialog) { $dialog.find('> div').addClass('half'); diff --git a/www/support/ui.js b/www/support/ui.js index c17bd9f5b..0519530da 100644 --- a/www/support/ui.js +++ b/www/support/ui.js @@ -66,11 +66,20 @@ define([ var $cat = $form.find('.cp-support-form-category'); var $title = $form.find('.cp-support-form-title'); var $content = $form.find('.cp-support-form-msg'); - // XXX block submission until pending uploads are complete? + // TODO block submission until pending uploads are complete? var $attachments = $form.find('.cp-support-attachments'); + var category = $cat.val().trim(); + /* + // || ($form.closest('.cp-support-list-ticket').data('cat') || "").trim(); + // Messages.support_formCategoryError = "Error: category is empty"; // TODO ensure this is translated before use + + if (!category) { + console.log($cat); + return void UI.alert(Messages.support_formCategoryError); + } + */ - var category = $cat.val().trim(); // XXX make category a required field? var title = $title.val().trim(); if (!title) { return void UI.alert(Messages.support_formTitleError); @@ -117,6 +126,7 @@ define([ }); var dropdownCfg = { text: Messages.support_category, + angleDown: 1, options: categories, container: $(container), isSelect: true @@ -178,8 +188,6 @@ define([ }).on('change', function (e) { var files = Util.slice(e.target.files); files.forEach(function (file) { - // XXX validate that the href is hosted on the same instance - // use relative URLs or compare it against a list or allowed domains? var ev = {}; ev.callback = function (data) { var x, a; @@ -320,10 +328,11 @@ define([ var attachments = (content.attachments || []).map(function (obj) { if (!obj || !obj.name || !obj.href) { return; } + // only support files explicitly beginning with /file/ so that users can't link outside of the instance + if (!/^\/file\//.test(obj.href)) { return; } var a = h('a', { href: '#' }, obj.name); - // XXX disallow remote URLs $(a).click(function (e) { e.preventDefault(); ctx.common.openURL(obj.href); diff --git a/www/teams/inner.js b/www/teams/inner.js index 86b8acf47..30612f3c3 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -418,6 +418,10 @@ define([ ])); common.displayAvatar($(avatar), team.metadata.avatar, team.metadata.name); $(btn).click(function () { + if (team.error) { + UI.warn(Messages.error); // XXX better error message - roster bug, can't load the team for now + return; + } openTeam(common, id, team); }); });