diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 3104fab18..0a0fd610e 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -136,8 +136,9 @@ define(req, function(AppConfig, Default, Language) { } }; - Messages.team_autoTrim = "Removing unused history... Please wait."; // XXX + Messages.admin_mfa_confirm_enable = "Are you sure you want to enable Multi-Factor Authentication?"; // XXX + Messages.admin_mfa_confirm_disable = "Are you sure you want to disable Multi-Factor Authentication?"; // XXX return Messages; diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index 239f73287..424e12519 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -953,6 +953,9 @@ li, li .fa, li .cptools { cursor: pointer; border-radius: @variables_radius; + &:focus { + outline: @cryptpad_color_brand solid 2px; + } } &> p { display: flex; diff --git a/customize.dist/src/less2/include/icons.less b/customize.dist/src/less2/include/icons.less index e3a273a92..bf3e3afc3 100644 --- a/customize.dist/src/less2/include/icons.less +++ b/customize.dist/src/less2/include/icons.less @@ -33,8 +33,8 @@ color: @cp_drive-fg; } &.cp-icons-element-selected { - background: @cp_drive-icon-hover; color: @cp_drive-fg; + outline: @cryptpad_color_brand solid 2px; } .fa, .cptools { display: block; diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index fbaaf4b5e..8bc3c3d85 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -28,6 +28,37 @@ max-height: 250px; } } + #cp-sidebarlayout-container { + #cp-sidebarlayout-rightside { + .cp-sidebarlayout-element[data-item] { + div#cp-admin-table-container { + overflow-x: auto; + .cp-sidebar-table#cp-admin-table { + tr { + display: flex; + } + th, td { + flex: 1; + word-wrap: break-word; + white-space: normal; + min-width: 13rem; + margin-right: 0px; + } + @media (max-width: @browser_media-not-small) { + width: 100%; + tr { + width: 100%; + } + th, td { + margin-right: 0; + min-width: 7rem; + } + } + } + } + } + } +} .cp-admin-color-current { width: 20px; height: 20px; diff --git a/www/admin/inner.js b/www/admin/inner.js index 3ca4f173f..a7b587266 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -578,23 +578,38 @@ define([ return APP.instanceStatus.enforceMFA; }, query: function (val, setState) { - sFrameChan.query('Q_ADMIN_RPC', { - cmd: 'ADMIN_DECREE', - data: ['ENFORCE_MFA', [val]] - }, function (e, response) { - if (e || response.error) { - UI.warn(Messages.error); - console.error(e, response); - } - APP.updateStatus(function () { - setState(APP.instanceStatus.enforceMFA); - flushCache(); + var isChecked = APP.instanceStatus.enforceMFA; + function showConfirmation(isChecked, setState) { + const confirmationContent = isChecked ? Messages.admin_mfa_confirm_disable : Messages.admin_mfa_confirm_enable; + UI.confirm(confirmationContent, function (confirmed) { + if (!confirmed) { + // User canceled their changes, restore the checkbox value + setState(isChecked); + return; + } + // User confirmed their changes, call the command and update the state + sFrameChan.query('Q_ADMIN_RPC', { + cmd: 'ADMIN_DECREE', + data: ['ENFORCE_MFA', [val]] + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); + } else { + APP.updateStatus(function () { + setState(APP.instanceStatus.enforceMFA); + flushCache(); + }); + } + }); }); - }); - }, + } + showConfirmation(isChecked, setState); + } }); + var getInstanceString = function (attr) { var val = APP.instanceStatus[attr]; var type = typeof(val); @@ -1101,6 +1116,9 @@ define([ "" ]; var list = blocks.table(header, []); + list.setAttribute('id', 'cp-admin-table'); + let div = blocks.block([list]); + div.setAttribute('id', 'cp-admin-table-container'); var nav = blocks.nav([button, refreshButton]); var form = blocks.form([ @@ -1201,7 +1219,7 @@ define([ }); }); - cb([form, list]); + cb([form, div]); }); var getBlockId = (val) => { @@ -1413,6 +1431,9 @@ define([ "" ]; var list = blocks.table(header, []); + list.setAttribute('id', 'cp-admin-table'); + let div = blocks.block([list]); + div.setAttribute('id', 'cp-admin-table-container'); var nav = blocks.nav([button, refreshButton]); @@ -1581,7 +1602,7 @@ define([ }); }); - cb([form, list]); + cb([form, div]); }); // Msg.admin_defaultlimitHint, .admin_defaultlimitTitle @@ -1741,6 +1762,9 @@ define([ Messages.admin_note ]; var table = blocks.table(header, []); + table.setAttribute('id', 'cp-admin-table'); + let div = blocks.block([table]); + div.setAttribute('id', 'cp-admin-table-container'); let $table = $(table).hide(); APP.refreshLimits = function () { @@ -1798,7 +1822,7 @@ define([ }); }; APP.refreshLimits(); - cb(table); + cb(div); }); // Msg.admin_accountMetadataHint.admin_accountMetadataTitle @@ -3594,6 +3618,9 @@ define([ ]; var table = blocks.table(header, []); + table.setAttribute('id', 'cp-admin-table'); + let div = blocks.block([table]); + div.setAttribute('id', 'cp-admin-table-container'); const onRefresh = function () { sFrameChan.query('Q_ADMIN_RPC', { @@ -3626,7 +3653,7 @@ define([ onRefresh(); onRefreshPerformance.reg(onRefresh); - cb(table); + cb(div); }); diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index bdf59fb6d..91974d210 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -157,6 +157,11 @@ border-radius: @variables_radius_L; } .tui-full-calendar-popup-container { + .tui-full-calendar-section-allday{ + &:focus { + outline: @cryptpad_color_brand solid 2px; + } + } min-width: 100%; background: @cp_flatpickr-bg; color: @cryptpad_text_col; @@ -208,6 +213,9 @@ text-overflow: ellipsis; font: @colortheme_app-font; padding: 0 10px; + &:focus{ + outline: @cryptpad_color_brand solid 2px; + } } input { flex: 1; } } @@ -403,6 +411,11 @@ & > input { height: 40px; } + .tui-full-calendar-content{ + &:focus{ + outline: @cryptpad_color_brand solid 2px; + } + } } // margin-bottom: 20px; input { diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 9cac57978..267b69958 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -2063,6 +2063,15 @@ APP.recurrenceRule = { editor.setOption('readOnly', false); editor.setOption('autoRefresh', true); editor.setOption('gutters', []); + editor.on('keydown', function (editor, e) { + if (e.which === 27) { + let $next = $(e.target).closest('.tui-full-calendar-popup-section').next(); + if ($next.length) { + $next.find('#tui-full-calendar-schedule-start-date').focus(); + } + e.stopPropagation(); + } + }); cm.configureTheme(common, function () {}); editor.setValue(oldEventBody); @@ -2147,6 +2156,13 @@ APP.recurrenceRule = { $el.find('input').attr('autocomplete', 'off'); $el.find('.tui-full-calendar-dropdown-button').addClass('btn btn-secondary'); $el.find('.tui-full-calendar-popup-close').addClass('btn btn-cancel fa fa-times cp-calendar-close').empty(); + $el.find('.tui-full-calendar-section-allday').attr('tabindex', 0); + $el.find('.cp-calendar-close').attr('tabindex',-1); + $el.find('.tui-full-calendar-section-allday').keydown(function (e) { + if (e.which === 13) { + $(this).click(); + } + }); var $container = $el.closest('.tui-full-calendar-floating-layer'); $container.addClass('cp-calendar-popup-flex'); @@ -2209,6 +2225,7 @@ APP.recurrenceRule = { setFormat(allDay); }); }); + UI.addTabListener(el); }; var onCalendarEditPopup = function (el) { var $el = $(el); @@ -2244,6 +2261,7 @@ APP.recurrenceRule = { var data = ev.schedule || {}; var id = data.id; + UI.addTabListener(el); if (!id) { return; } if (id.indexOf('|') === -1) { return; } // Original event ID doesn't contain | diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 2103f3f82..19498d83a 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -164,12 +164,12 @@ define([ dialog.okButton = function (content, classString) { var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.btn.ok.primary'; - return h(sel, { tabindex: '2', }, content || Messages.okButton); + return h(sel, content || Messages.okButton); }; dialog.cancelButton = function (content, classString) { var sel = typeof(classString) === 'string'? 'button.' + classString:'button.btn.cancel'; - return h(sel, { tabindex: '1'}, content || Messages.cancelButton); + return h(sel, content || Messages.cancelButton); }; dialog.message = function (text) { @@ -577,7 +577,7 @@ define([ return frame; }; - let addTabListener = frame => { + let addTabListener = UI.addTabListener = frame => { // find focusable elements let modalElements = $(frame).find('a, button, input, [tabindex]:not([tabindex="-1"]), textarea').filter(':visible').filter(':not(:disabled)'); @@ -670,6 +670,7 @@ define([ $modal: $blockContainer, show: function () { $blockContainer.css('display', 'flex'); + addTabListener($blockContainer); }, hide: hide }; @@ -719,6 +720,7 @@ define([ Notifier.notify(); }); + addTabListener(frame); return { element: frame, delete: close @@ -770,7 +772,7 @@ define([ document.body.appendChild(frame); setTimeout(function () { - $(input).select().focus(); + addTabListener(frame); Notifier.notify(); }); }; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 5405bf319..10db8c37c 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3559,7 +3559,7 @@ define([ $(link).click(function (e) { e.preventDefault(); e.stopPropagation(); - var obj = { pw: msg.content.password || '' }; + var obj = { pw: msg.content.password || '', f: 1 }; common.openURL(Hash.getNewPadURL(msg.content.href, obj)); }); @@ -3615,7 +3615,7 @@ define([ // Add the pad to your drive // This command will also add your mailbox to the metadata log - // The callback is called when the pad is stored, independantly of the metadata command + // The callback is called when the pad is stored, independently of the metadata command if (data.calendar) { var calendarModule = common.makeUniversal('calendar'); var calendarData = data.calendar; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 4525c8287..e2d6a3746 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2619,7 +2619,8 @@ define([ disableCache: localStorage['CRYPTPAD_STORE|disableCache'], driveEvents: !rdyCfg.noDrive, //rdyCfg.driveEvents // Boolean lastVisit: Number(localStorage.lastVisit) || undefined, - blockId: blockId + blockId: blockId, + blockHash: blockHash }; common.userHash = userHash || LocalStore.getUserHash(); diff --git a/www/common/notifications.js b/www/common/notifications.js index 82fb4b45d..8ec80c2e9 100644 --- a/www/common/notifications.js +++ b/www/common/notifications.js @@ -129,6 +129,7 @@ define([ var obj = { p: msg.content.isTemplate ? ['template'] : undefined, t: teamNotification || undefined, + f: 1, pw: msg.content.password || '' }; common.openURL(Hash.getNewPadURL(msg.content.href, obj)); diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index cfa749084..906be2843 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -8,7 +8,8 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/components/chainpad-crypto/crypto.js', -], function (ApiConfig, Messaging, Hash, Util, Crypto) { + '/common/outer/login-block.js', + ], function (ApiConfig, Messaging, Hash, Util, Crypto, Block) { // Random timeout between 10 and 30 times your sync time (lag + chainpad sync) var getRandomTimeout = function (ctx) { @@ -237,6 +238,14 @@ define([ cb(true); }; + // Encrypt the password under the right key before sending it via URL hash + var encryptPassword = function(ctx, password) { + let uHash = ctx.store.data.blockHash; + let uSecret = Block.parseBlockHash(uHash); + let key = uSecret.keys.symmetric; + return Crypto.encrypt(password, key); + }; + // Hide duplicates when receiving a SHARE_PAD notification: // Keep only one notification per channel: the stronger and more recent one var channels = {}; @@ -265,8 +274,7 @@ define([ } if (content.password) { - var key = ctx.store.driveSecret.keys.cryptKey; - content.password = Crypto.encrypt(content.password, key); + content.password = encryptPassword(ctx, content.password); } // Update the data @@ -384,8 +392,8 @@ define([ var channel = content.channel || content.teamChannel; if (content.password) { - var key = ctx.store.driveSecret.keys.cryptKey; - content.password = Crypto.encrypt(content.password, key); + content.pw = content.password; + content.password = encryptPassword(ctx, content.password); } if (addOwners[channel]) { return void cb(true); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 6c46ced04..b4031d155 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1083,7 +1083,7 @@ define([ Cryptpad.addSharedFolder(null, secret, cb); } else { var _data = { - password: data.password, + password: data.pw || data.password, href: data.href, channel: data.channel, title: data.title, @@ -1359,7 +1359,7 @@ define([ var viewH = Utils.Hash.getViewHashFromKeys(_secret); var href = Utils.Hash.hashToHref(editH, parsed.type); var roHref = Utils.Hash.hashToHref(viewH, parsed.type); - Cryptpad.setPadAttribute('password', password, w(), parsed.getUrl()); + Cryptpad.setPadAttribute('password', pw, w(), parsed.getUrl()); Cryptpad.setPadAttribute('channel', chan, w(), parsed.getUrl()); Cryptpad.setPadAttribute('href', href, w(), parsed.getUrl()); Cryptpad.setPadAttribute('roHref', roHref, w(), parsed.getUrl()); diff --git a/www/form/app-form.less b/www/form/app-form.less index c9e77e64b..00fdfafb2 100644 --- a/www/form/app-form.less +++ b/www/form/app-form.less @@ -1011,7 +1011,6 @@ min-width: 300px; width: 300px; position: absolute; - left: 20px; word-break: break-word; background: @cp_form-bg1; z-index: 2; diff --git a/www/teams/inner.js b/www/teams/inner.js index 99157708d..1c231a1a5 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -516,7 +516,7 @@ define([ created++; } if (team.empty) { - var createTeamDiv = h('div.cp-team-list-team.empty'+createCls,{ + var createTeamDiv = h('li.cp-team-list-team.empty'+createCls,{ tabindex: '0' }, [ h('span.cp-team-list-name.empty', Messages.team_listSlot), @@ -533,7 +533,7 @@ define([ return; } var avatar = h('span.cp-avatar'); - var teamDiv = h('div.cp-team-list-team',{ + var teamDiv = h('li.cp-team-list-team',{ tabindex: '0' }, [ h('span.cp-team-list-avatar', avatar), @@ -553,7 +553,7 @@ define([ } }); }); - content.push(h('div.cp-team-list-container', list)); + content.push(h('ul.cp-team-list-container', list)); cb(content); }); return content;