diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 715ab1e91..cc11f1be2 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -728,7 +728,7 @@ define([ Pages['/profile/'] = Pages['/profile/index.html'] = function () { return [ - h('div#toolbar'), + h('div#cp-toolbar'), h('div#container'), loadingScreen() ]; diff --git a/customize.dist/src/less/variables.less b/customize.dist/src/less/variables.less index 4d804abea..1d3415d89 100644 --- a/customize.dist/src/less/variables.less +++ b/customize.dist/src/less/variables.less @@ -102,9 +102,9 @@ @category-bg: #f4f4f4; -@button-bg: #3066e5; -@button-alt-bg: #fff; -@button-red-bg: #e54e4e; +@button-bg: @colortheme_sidebar-button-bg; +@button-alt-bg: @colortheme_sidebar-button-alt-bg; +@button-red-bg: @colortheme_sidebar-button-red-bg; .unselectable () { -webkit-touch-callout: none; diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index d3db9e7cd..4893f5fa2 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -79,7 +79,7 @@ @colortheme_todo-bg: #7bccd1; @colortheme_todo-color: #000; -// Sidebar layout +// Sidebar layout (profile / settings) @colortheme_sidebar-active: #fff; @colortheme_sidebar-left-bg: #eee; @colortheme_sidebar-left-fg: #000; @@ -87,7 +87,9 @@ @colortheme_sidebar-right-bg: #fff; @colortheme_sidebar-right-fg: #000; @colortheme_sidebar-description: #777; - +@colortheme_sidebar-button-bg: #3066e5; +@colortheme_sidebar-button-red-bg: #e54e4e; +@colortheme_sidebar-button-alt-bg: #fff; @cryptpad_color_blue: #4591C4; @cryptpad_color_grey: #999999; diff --git a/customize.dist/src/less2/include/sidebar-layout.less b/customize.dist/src/less2/include/sidebar-layout.less new file mode 100644 index 000000000..e0bec9dcf --- /dev/null +++ b/customize.dist/src/less2/include/sidebar-layout.less @@ -0,0 +1,99 @@ +@import (once) "/customize/src/less2/include/colortheme.less"; +@import (once) "/customize/src/less2/include/leftside-menu.less"; + +@leftside-bg: @colortheme_sidebar-left-bg; +@leftside-color: @colortheme_sidebar-left-fg; +@rightside-color: @colortheme_sidebar-right-fg; +@description-color: @colortheme_sidebar-description; + +@button-width: 400px; + + +.sidebar-layout_main() { + input[type="text"] { + padding-left: 10px; + } + #cp-sidebarlayout-container { + font-size: 16px; + display: flex; + flex: 1; + min-height: 0; + #cp-sidebarlayout-leftside { + color: @leftside-color; + width: 250px; + background: @leftside-bg; + display: flex; + flex-flow: column; + .cp-sidebarlayout-categories { + flex: 1; + .cp-sidebarlayout-category { + .leftside-menu-category_main(); + } + } + } + #cp-sidebarlayout-rightside { + flex: 1; + padding: 5px 20px; + color: @rightside-color; + overflow: auto; + + // Following rules are only in settings + .element { + label:not(.noTitle), .label { + display: block; + font-weight: bold; + margin-bottom: 0; + } + .description { + display: block; + color: @description-color; + margin-bottom: 5px; + } + margin-bottom: 20px; + } + [type="text"], button { + vertical-align: middle; + height: 40px; + box-sizing: border-box; + } + .inputBlock { + display: inline-flex; + width: @button-width; + input { + flex: 1; + border-radius: 0.25em 0 0 0.25em; + border: 1px solid #adadad; + border-right: 0px; + } + button { + border-radius: 0 0.25em 0.25em 0; + //border: 1px solid #adadad; + border-left: 0px; + } + } + &>div { + margin: 10px 0; + } + button.btn { + @button-bg: @colortheme_sidebar-button-bg; + @button-red-bg: @colortheme_sidebar-button-red-bg; + background-color: @button-bg; + border-color: darken(@button-bg, 10%); + color: white; + &:hover { + background-color: darken(@button-bg, 10%); + } + &.btn-danger { + background-color: @button-red-bg; + border-color: darken(@button-red-bg, 10%); + color: white; + &:hover { + background-color: darken(@button-red-bg, 10%); + } + } + } + } + } +} + + diff --git a/customize.dist/src/less2/main.less b/customize.dist/src/less2/main.less index c000248ba..38c242b10 100644 --- a/customize.dist/src/less2/main.less +++ b/customize.dist/src/less2/main.less @@ -34,4 +34,5 @@ body.cp-app-contacts { @import "../../../contacts/app-contacts.less"; } body.cp-app-poll { @import "../../../poll/app-poll.less"; } body.cp-app-whiteboard { @import "../../../whiteboard/app-whiteboard.less"; } body.cp-app-todo { @import "../../../todo/app-todo.less"; } +body.cp-app-profile { @import "../../../profile/app-profile.less"; } diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 06fb7f13b..9c330f651 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -44,6 +44,7 @@ define(function () { out.typing = "Édition"; out.initializing = "Initialisation..."; out.forgotten = 'Déplacé vers la corbeille'; + out.errorState = 'Erreur critique : {0}'; out.lag = 'Latence'; out.readonly = 'Lecture seule'; out.anonymous = "Anonyme"; @@ -56,6 +57,7 @@ define(function () { out.viewers = "lecteurs"; out.editor = "éditeur"; out.editors = "éditeurs"; + out.userlist_offline = "Vous êtes actuellement hors-ligne, la liste des utilisateurs n'est pas disponible."; out.language = "Langue"; @@ -486,6 +488,13 @@ define(function () { out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive"; out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles."; + out.settings_thumbnails = "Vignettes"; + out.settings_disableThumbnailsAction = "Désactiver la création de vignettes dans CryptDrive"; + out.settings_disableThumbnailsDescription = "Des vignettes de vos pads sont automatiquement créées et stockées dans votre navigateur. Vous pouvez désactiver cette fonctionnalité."; + out.settings_resetThumbnailsAction = "Nettoyer"; + out.settings_resetThumbnailsDescription = "Nettoyer toutes les vignettes stockées dans votre navigateur."; + out.settings_resetThumbnailsDone = "Toutes les vignettes ont été effacées."; + out.settings_importTitle = "Importer les pads récents de ce navigateur dans votre CryptDrive"; out.settings_import = "Importer"; out.settings_importConfirm = "Êtes-vous sûr de vouloir importer les pads récents de ce navigateur dans le CryptDrive de votre compte utilisateur ?"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 9e9531627..fbd7cdbbf 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -43,9 +43,10 @@ define(function () { out.disconnected = 'Disconnected'; out.synchronizing = 'Synchronizing'; out.reconnecting = 'Reconnecting...'; - out.typing = "Typing"; + out.typing = "Editing"; out.initializing = "Initializing..."; out.forgotten = 'Moved to the trash'; + out.errorState = 'Critical error: {0}'; out.lag = 'Lag'; out.readonly = 'Read only'; out.anonymous = "Anonymous"; @@ -58,6 +59,7 @@ define(function () { out.viewers = "viewers"; out.editor = "editor"; out.editors = "editors"; + out.userlist_offline = "You're currently offline, the user list is not available."; out.language = "Language"; @@ -491,6 +493,13 @@ define(function () { out.settings_resetTipsButton = "Reset the available tips in CryptDrive"; out.settings_resetTipsDone = "All the tips are now visible again."; + out.settings_thumbnails = "Thumbnails"; + out.settings_disableThumbnailsAction = "Disable thumbnails creation in your CryptDrive"; + out.settings_disableThumbnailsDescription = "Thumbnails are automatically created and stored in your browser when you visit a new pad. You can disable this feature here."; + out.settings_resetThumbnailsAction = "Clean"; + out.settings_resetThumbnailsDescription = "Clean all the pads thumbnails stored in your browser."; + out.settings_resetThumbnailsDone = "All the thumbnails have been erased."; + out.settings_importTitle = "Import this browser's recent pads in your CryptDrive"; out.settings_import = "Import"; out.settings_importConfirm = "Are you sure you want to import recent pads from this browser to your user account's CryptDrive?"; diff --git a/www/code/inner.js b/www/code/inner.js index 7296d07d3..5f2cf3df1 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -7,7 +7,6 @@ define([ '/common/sframe-common.js', '/common/sframe-app-framework.js', '/common/common-util.js', - '/common/common-thumbnail.js', '/common/modes.js', 'cm/lib/codemirror', @@ -46,7 +45,6 @@ define([ SFCommon, Framework, Util, - Thumb, Modes, CMeditor) { diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index fb7edafc4..dd8e37ec0 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -1,11 +1,17 @@ define([ + '/common/common-util.js', + '/common/visible.js', + '/common/common-hash.js', + '/file/file-crypto.js', + '/bower_components/localforage/dist/localforage.min.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function () { +], function (Util, Visible, Hash, FileCrypto, localForage) { var Nacl = window.nacl; var Thumb = { dimension: 100, padDimension: 200, - UPDATE_INTERVAL: 5000 + UPDATE_INTERVAL: 60000, + UPDATE_FIRST: 5000 }; var supportedTypes = [ @@ -189,5 +195,82 @@ define([ require(['/bower_components/html2canvas/build/html2canvas.min.js'], todo); }; + Thumb.initPadThumbnails = function (opts) { + if (!opts.href || !opts.getContent) { + throw new Error("href and getContent are needed for thumbnails"); + } + var oldThumbnailState; + var mkThumbnail = function () { + var content = opts.getContent(); + if (content === oldThumbnailState) { return; } + Thumb.fromDOM(opts, function (err, b64) { + oldThumbnailState = content; + Thumb.setPadThumbnail(opts.href, b64); + }); + }; + var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL); + var to; + var tUntil; + var interval = function () { + tUntil = nafa(); + if (tUntil) { + window.clearTimeout(to); + to = window.setTimeout(interval, tUntil+1); + return; + } + to = window.setTimeout(interval, Thumb.UPDATE_INTERVAL+1); + }; + Visible.onChange(function (v) { + if (v) { + window.clearTimeout(to); + return; + } + interval(); + }); + if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); } + }; + + var addThumbnail = function (err, thumb, $span, cb) { + var img = new Image(); + img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:;base64,'+thumb; + $span.find('.cp-icon').hide(); + $span.prepend(img); + cb($(img)); + }; + Thumb.setPadThumbnail = function (href, b64, cb) { + cb = cb || function () {}; + var k ='thumbnail-' + href; + localForage.setItem(k, b64, cb); + }; + Thumb.displayThumbnail = function (href, $container, cb) { + cb = cb || function () {}; + var parsed = Hash.parsePadUrl(href); + var k ='thumbnail-' + href; + var whenNewThumb = function () { + var secret = Hash.getSecrets('file', parsed.hash); + var hexFileName = Util.base64ToHex(secret.channel); + var src = Hash.getBlobPathFromHex(hexFileName); + var cryptKey = secret.keys && secret.keys.fileKeyStr; + var key = Nacl.util.decodeBase64(cryptKey); + FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { + if (!metadata.thumbnail) { + return void localForage.setItem(k, 'EMPTY'); + } + localForage.setItem(k, metadata.thumbnail, function (err) { + addThumbnail(err, metadata.thumbnail, $container, cb); + }); + }); + }; + localForage.getItem(k, function (err, v) { + if (!v && parsed.type === 'file') { + // We can only create thumbnails for files here since we can't easily decrypt pads + return void whenNewThumb(); + } + if (!v) { return; } + if (v === 'EMPTY') { return; } + addThumbnail(err, v, $container, cb); + }); + }; + return Thumb; }); diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index bf446fe37..3820f3125 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -129,6 +129,7 @@ define([ var domFromHTML = function (html) { var Dom = new DOMParser().parseFromString(html, "text/html"); + Dom.normalize(); removeForbiddenTags(Dom.body); removeListeners(Dom.body); return Dom; @@ -179,6 +180,7 @@ define([ var $div = $('