diff --git a/.jshintignore b/.jshintignore index 3dce1dfc9..9a7d8b1c7 100644 --- a/.jshintignore +++ b/.jshintignore @@ -19,6 +19,7 @@ www/pad/wysiwygarea-plugin.js www/pad/mediatag-plugin.js www/pad/mediatag-plugin-dialog.js www/pad/disable-base64.js +www/pad/wordcount/ www/kanban/jkanban.js www/common/jscolor.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 714d02d59..12df2ec6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,43 @@ -# Elasmotherium release notes +# FalklandWolf release (3.5.0) + +## Goals + +This release features work that we've been planning for a long time centered around sharing collections of documents in a more granular way. + +This is our first release since David Benqué joined our team, so in addition to these team-centric updates we also worked on integrating some UI/UX improvements. + +## Update notes + +Updating to 3.5.0 from 3.4.0 is simple. + +1. stop your server +2. pull the latest code via git +3. run `bower update` +4. restart your server + +## Features + +* We restyled some elements throughout the platform: + * our tooltips have a sleeker flat design + * the quota bar which appears in the drive, teams, and settings pages has also been improved + * we've begun improving the look and feel of various popup dialogs +* We've added support for password-change for owned uploaded files and owned shared folders: + * changing passwords for encrypted files means that the original file will be removed from the server and a new file will be encrypted with a new key and uploaded to a new location on the server. References to the original file will be broken. This includes links, media-tags embedded within pads, and items in other users' drives or shared folders to which you do not have access. + * the process is very similar for shared folders stored in users' CryptDrives, except that users will have the opportunity to enter the new password when they visit the platform. +* We're very happy to finally introduce the notion of _read-only shared folders_. While we've had the capacity to make shared folders read-only for some time, it was only in the same sense as pads were read-only. + * This is to say that while a viewer cannot modify the document, any links to encrypted documents within that document would confer their natural editing rights to viewers, making it possible to accidentally leak access when a single pad was shared. + * Our new read-only shared folders encrypt the editing keys for the documents they contain, such that only those with the ability to change the folder structure itself have the inherent capacity to edit the documents contained within. We think this is more intuitive than the alternative, but it took a lot of work to make it happen! + * Unfortunately, older shared folders created before this release will already contain the cryptographic keys which confer editing rights. Pads which are added to shared folders from this release onward will have the keys for their editing rights encrypted. We'll offer the ability for owners to migrate these shared folders in an upcoming release once we've added the ability to selectively trim document history. +* Similarly, we've introduced the notion of _viewers_ in teams. Viewers are listed in the team roster and have the ability to view the contents of the team's drive, but not to edit them or add new documents. + * Unfortunately, the notion of viewers is also complicated by the fact that documents added to team drives or shared folders in team drives did not have their editing keys encrypted. The first team member to open the team drive since we've deployed this release will run a migration that will encrypt the keys saved within the team drive, however, the encryption keys will remain in the drive's history until we develop a means of selectively trimming history. + +## Bug fixes + +* We discovered and fixed some bugs in the serverside code responsible for handling some aspects of file upload related to starting a new upload after having cancelled a previous session. +* We also identified a regression in Our _slides_ app related to the rendering of `
` tags, such as you might create with a `****` sequence in the corresponding markdown. This was introduced with some overly broad CSS that was intended to style our notifications page. We've since made the notifications styles more specific such that they can't interfere with other applications. +* We've become aware of some mysterious behaviour in Firefox that seems to cause some tabs or functionality to reconnect to the server after going offline while other aspects of the platform did not. Until now we've always assumed that users were connected or not, and this partial connection has revealed some bugs in our implementation. Consequently, we've begun adding some measures to detect odd behaviour if it occurs. We expect to have determined the cause of this behaviour and to have proposed a solution by our next release. + +# Elasmotherium release (3.4.0) ## Goals diff --git a/customize.dist/ckeditor-config.js b/customize.dist/ckeditor-config.js index dc138a558..5c4940c52 100644 --- a/customize.dist/ckeditor-config.js +++ b/customize.dist/ckeditor-config.js @@ -10,7 +10,7 @@ CKEDITOR.editorConfig = function( config ) { // document itself and causes problems when it's sent across the wire and reflected back config.removePlugins= 'resize,elementspath'; config.resize_enabled= false; //bottom-bar - config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64,mathjax'; + config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64,mathjax,wordcount'; config.toolbarGroups= [ // {"name":"clipboard","groups":["clipboard","undo"]}, //{"name":"editing","groups":["find","selection"]}, diff --git a/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less index 142cc2509..9cd904c45 100644 --- a/customize.dist/src/less2/include/alertify.less +++ b/customize.dist/src/less2/include/alertify.less @@ -17,6 +17,7 @@ @alertify-btn-fg: @alertify-fore; + @alertify-light-bg: fade(@alertify-fore, 25%); @alertify-bg: @colortheme_modal-dim; @alertify-fg: @alertify-fore; @@ -164,27 +165,29 @@ display: flex; flex-flow: column; .alertify-tabs-titles { - height: 30px; + height: 40px; display: flex; border-bottom: 1px solid @alertify-fore; - margin-bottom: 20px; + margin-bottom: 10px; box-sizing: content-box; span { font-size: 20px; - height: 30px; - line-height: 30px; + height: 40px; + line-height: 40px; box-sizing: border-box; padding: 0 15px; border-left: 1px solid lighten(@alertify-base, 10%); border-right: 1px solid lighten(@alertify-base, 10%); cursor: pointer; + &:hover { + background-color: @alertify-light-bg; + } } span.alertify-tabs-active { background-color: @alertify-fore; border-left: 1px solid @alertify-fore; border-right: 1px solid @alertify-fore; color: @alertify-base; - font-weight: bold; cursor: default; } } @@ -206,24 +209,28 @@ } ::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ - color: darken(@alertify-input-fg, 15%); + color: @cryptpad_color_grey; opacity: 1; /* Firefox */ } :-ms-input-placeholder { /* Internet Explorer 10-11 */ - color: darken(@alertify-input-fg, 15%); + color: @cryptpad_color_grey; } ::-ms-input-placeholder { /* Microsoft Edge */ - color: darken(@alertify-input-fg, 15%); + color: @cryptpad_color_grey; } input:not(.form-control), textarea { - background-color: @alertify-input-bg; - color: @alertify-input-fg; - border: 0px; - + background-color: @alertify-input-fg; + color: @cryptpad_text_col; + border: 1px solid @alertify-input-bg; margin-bottom: 15px; width: 100%; font-size: 100%; padding: @alertify_padding-base; + &[readonly] { + background-color: @alertify-light-bg; + color: @cryptpad_text_col; + border-color: @alertify-input-fg; + } } span.cp-password-container { @@ -239,6 +246,7 @@ } } + input[type="checkbox"], input[type="radio"] { width: auto; padding: 0; @@ -274,12 +282,16 @@ border-radius: 0; color: @alertify-btn-fg; - border: 1px solid @colortheme_alertify-cancel-border; + border: 1px solid @alertify-btn-fg; &.no-margin { margin: 0; } + &:hover, &:active { + background-color: @alertify-light-bg; + } + &.safe, &.danger { color: @colortheme_old-base; white-space: normal; @@ -313,10 +325,16 @@ } } - &:hover, &:active { - background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-cancel, 10%), lighten(@colortheme_alertify-cancel, 10%)); + &.cancel { + border-color: @colortheme_alertify-cancel-border; + color: @colortheme_alertify-cancel-border; + &:hover, &:hover { + background-color: fade(@colortheme_alertify-cancel-border, 25%); + } } + + &:focus { //border: 1px dotted @alertify-base; box-shadow: 0px 0px 5px @colortheme_alertify-primary; @@ -337,12 +355,15 @@ } } - button.btn { - margin: 6px 4px; - } - nav { + padding: @alertify_padding-base; text-align: right; + button { + margin: 0px !important; + &:not(:last-child) { + margin-right: @alertify_padding-base !important; + } + } } } } diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index 44719c2d6..23dca6fe8 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -23,7 +23,7 @@ @colortheme_form-bg: @colortheme_logo-2; @colortheme_form-color: #ffffff; @colortheme_form-bg-alt: #ffffff; -@colortheme_form-color-alt: @colortheme_logo-1; +@colortheme_form-color-alt: @colortheme_logo-2; @colortheme_form-warning: #f49842; @colortheme_form-warning-hov: darken(@colortheme_form-warning, 5%); @@ -56,7 +56,7 @@ @colortheme_alertify-disabled-text: #ffffff; @colortheme_alertify-disabled-border: #6c757d; @colortheme_alertify-cancel: @colortheme_modal-bg; -@colortheme_alertify-cancel-border: #ccc; +@colortheme_alertify-cancel-border: #949494; @colortheme_notification-log: fade(@colortheme_logo-1, 90%); @colortheme_notification-color: #fff;; diff --git a/customize.dist/src/less2/include/framework.less b/customize.dist/src/less2/include/framework.less index 685bfcaf5..0eefababb 100644 --- a/customize.dist/src/less2/include/framework.less +++ b/customize.dist/src/less2/include/framework.less @@ -15,6 +15,7 @@ @import (reference) "./messenger.less"; @import (reference) "./cursor.less"; @import (reference) "./usergrid.less"; +@import (reference) "./modals-ui-elements.less"; .framework_main(@bg-color, @warn-color, @color) { --LessLoader_require: LessLoader_currentFile(); @@ -32,6 +33,7 @@ @color: @color ); .alertify_main(); + .modals-ui-elements_main(); .corner_main(); .contextmenu_main(); .fileupload_main(); @@ -70,6 +72,7 @@ ); .fileupload_main(); .alertify_main(); + .modals-ui-elements_main(); .corner_main(); .contextmenu_main(); .tippy_main(); diff --git a/customize.dist/src/less2/include/modal.less b/customize.dist/src/less2/include/modal.less index 07c533b15..044763c59 100644 --- a/customize.dist/src/less2/include/modal.less +++ b/customize.dist/src/less2/include/modal.less @@ -66,6 +66,11 @@ padding: 8px 12px; margin: 1em; width: 300px; + &[type="text"] { + background-color: @colortheme_modal-input-fg; + color: @cryptpad_text_col; + border: 1px solid @colortheme_modal-input; + } } .cp-modal-close { diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less new file mode 100644 index 000000000..57c9b0a53 --- /dev/null +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -0,0 +1,26 @@ +@import (reference) "./colortheme-all.less"; + +.modals-ui-elements_main() { + --LessLoader_require: LessLoader_currentFile(); +} +& { + // Share modal + .msg.cp-inline-radio-group { + .radio-group { + display: flex; + flex-direction: row; + .cp-radio { + margin-right: 30px; + } + } + } + + // Properties modal + .cp-app-prop { + margin-bottom: 10px; + } + + .cp-app-prop-content { + color: @cryptpad_text_col; + } +} diff --git a/customize.dist/src/less2/include/password-input.less b/customize.dist/src/less2/include/password-input.less index a2f2fb044..79824bac7 100644 --- a/customize.dist/src/less2/include/password-input.less +++ b/customize.dist/src/less2/include/password-input.less @@ -9,8 +9,27 @@ flex: 1; min-width: 0; } - label, .fa { - margin-left: 10px; + .fa { + width: 30px; + height: 30px; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + &:hover { + background-color: rgba(0,0,0,0.1); + } + } + } + .cp-password-change-container { + display: flex; + align-items: center; + .cp-password-container { + margin-bottom: 0 !important; + flex: 1; + } + button { + margin: 0 !important; } } } diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less index ff588ff77..0df5823a0 100644 --- a/customize.dist/src/less2/include/toolbar.less +++ b/customize.dist/src/less2/include/toolbar.less @@ -507,7 +507,8 @@ } } .cp-toolbar-top { - @media screen and (max-width: @browser_media-medium-screen) { + @media screen and (max-width: @browser_media-medium-screen), + screen and (max-height: 500px) { flex-wrap: wrap; height: @toolbar_line-height; .cp-pad-not-pinned { diff --git a/customize.dist/src/less2/include/usergrid.less b/customize.dist/src/less2/include/usergrid.less index a2d2c8fc5..38202de86 100644 --- a/customize.dist/src/less2/include/usergrid.less +++ b/customize.dist/src/less2/include/usergrid.less @@ -24,7 +24,7 @@ min-width: 0; margin-bottom: 0 !important; &::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ - color: @colortheme_alertify-primary-text; + color: @cryptpad_color_grey; opacity: 1; /* Firefox */ } } diff --git a/docs/cryptpad-docker.md b/docs/cryptpad-docker.md index 12018902e..5998dc143 100644 --- a/docs/cryptpad-docker.md +++ b/docs/cryptpad-docker.md @@ -2,7 +2,7 @@ Cryptpad includes support for building a Docker image and running it to provide a Cryptpad instance. You can manage the container manually, or let Docker Compose manage it for you. -A full tutorial is available [on the Cryptpad Github wiki](https://github.com/xwiki-labs/cryptpad/wiki/Docker-(with-Nginx-and-Traefik)). This document provides a brief overview. +A full tutorial is available [on the Cryptpad Github wiki](https://github.com/xwiki-labs/cryptpad/wiki/Docker). This document provides a brief overview. ## Features diff --git a/www/common/common-constants.js b/www/common/common-constants.js index a083ceb90..553665574 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -18,8 +18,6 @@ define(['/customize/application_config.js'], function (AppConfig) { deprecatedKey: 'deprecated', MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 3, MAX_TEAMS_OWNED: AppConfig.maxOwnedTeams || 1, - // Sub - plan: 'CryptPad_plan', // Apps criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications'] }; diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 72851a8cc..04acc26d8 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -146,7 +146,7 @@ define([ type: 'text', 'class': 'cp-text-input', }, opt); - return h('input', attrs); + return h('p.msg', h('input', attrs)); }; dialog.nav = function (content) { @@ -191,6 +191,10 @@ define([ if (!tab.content || !tab.title) { return; } var content = h('div.alertify-tabs-content', tab.content); var title = h('span.alertify-tabs-title', tab.title); + if (tab.icon) { + var icon = h('i', {class: tab.icon}); + $(title).prepend(' ').prepend(icon); + } $(title).click(function () { titles.forEach(function (t) { $(t).removeClass('alertify-tabs-active'); }); contents.forEach(function (c) { $(c).removeClass('alertify-tabs-content-active'); }); @@ -344,7 +348,8 @@ define([ if (!b.name || !b.onClick) { return; } var button = h('button', { tabindex: '1', 'class': b.className || '' }, b.name); $(button).click(function () { - b.onClick(); + var noClose = b.onClick(); + if (noClose) { return; } var $modal = $(button).parents('.alertify').first(); if ($modal.length && $modal[0].closeModal) { $modal[0].closeModal(function () { @@ -590,9 +595,10 @@ define([ }, opts); var input = h('input.cp-password-input', attributes); - var reveal = UI.createCheckbox('cp-password-reveal', Messages.password_show); + //var reveal = UI.createCheckbox('cp-password-reveal', Messages.password_show); var eye = h('span.fa.fa-eye.cp-password-reveal'); + /* $(reveal).find('input').on('change', function () { if($(this).is(':checked')) { $(input).prop('type', 'text'); @@ -602,26 +608,41 @@ define([ $(input).prop('type', 'password'); $(input).focus(); }); + */ - $(eye).mousedown(function () { - $(input).prop('type', 'text'); - $(input).focus(); - }).mouseup(function(){ - $(input).prop('type', 'password'); - $(input).focus(); - }).mouseout(function(){ - $(input).prop('type', 'password'); - $(input).focus(); - }); if (displayEye) { + $(eye).mousedown(function () { + $(input).prop('type', 'text'); + $(input).focus(); + }).mouseup(function(){ + $(input).prop('type', 'password'); + $(input).focus(); + }).mouseout(function(){ + $(input).prop('type', 'password'); + $(input).focus(); + }); + } else { + $(eye).click(function () { + if ($(this).hasClass('fa-eye')) { + $(input).prop('type', 'text'); + $(input).focus(); + $(this).removeClass('fa-eye').addClass('fa-eye-slash'); + return; + } + $(input).prop('type', 'password'); + $(input).focus(); + $(this).removeClass('fa-eye-slash').addClass('fa-eye'); + }); + } + /*if (displayEye) { $(reveal).hide(); } else { $(eye).hide(); - } + }*/ return h('span.cp-password-container', [ input, - reveal, + //reveal, eye ]); }; @@ -991,6 +1012,7 @@ define([ if (e.which === 32) { e.stopPropagation(); e.preventDefault(); + if ($(input).is(':checked')) { return; } $(input).prop('checked', !$(input).is(':checked')); $(input).change(); } diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 58cadb920..2b8d36e81 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -571,7 +571,7 @@ define([ style: 'flex: 1;' }); var passwordOk = h('button', Messages.properties_changePasswordButton); - var changePass = h('span.cp-password-container', [ + var changePass = h('span.cp-password-change-container', [ newPassword, passwordOk ]); @@ -685,29 +685,20 @@ define([ })); } + // XXX problem with the hardcoded ": " here + // possibly duplicate translation keys if (data.tags && Array.isArray(data.tags)) { - $('