From 2be5db95409defe75259d7dcd1bc8cfe3d8bb044 Mon Sep 17 00:00:00 2001 From: yflory <yann.flory@xwiki.com> Date: Tue, 10 Jan 2017 15:04:02 +0100 Subject: [PATCH] Ability to open files in readonly mode Fix CSS issues --- customize.dist/src/less/toolbar.less | 7 +- customize.dist/toolbar.css | 9 +- customize.dist/translations/messages.fr.js | 1 + customize.dist/translations/messages.js | 1 + www/common/toolbar.js | 11 +- www/drive/file.css | 2 + www/drive/inner.html | 9 +- www/drive/main.js | 141 ++++++++++++++------- 8 files changed, 128 insertions(+), 53 deletions(-) diff --git a/customize.dist/src/less/toolbar.less b/customize.dist/src/less/toolbar.less index fa9bf2f60..2b5798594 100644 --- a/customize.dist/src/less/toolbar.less +++ b/customize.dist/src/less/toolbar.less @@ -91,8 +91,7 @@ } .cryptpad-state { - line-height: 30px; /* equivalent to 26px + 2*2px margin used for buttons */ - float: left; + line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */ } .rightside-button { @@ -324,6 +323,10 @@ text-transform: uppercase; } .cryptpad-toolbar-username { + line-height: 32px; + button { + line-height: initial; + } } .lag { height: 15px !important; diff --git a/customize.dist/toolbar.css b/customize.dist/toolbar.css index f3e44fb29..a6efcadb0 100644 --- a/customize.dist/toolbar.css +++ b/customize.dist/toolbar.css @@ -96,9 +96,8 @@ } } .cryptpad-toolbar .cryptpad-state { - line-height: 30px; + line-height: 32px; /* equivalent to 26px + 2*2px margin used for buttons */ - float: left; } .cryptpad-toolbar .rightside-button { float: right; @@ -332,6 +331,12 @@ font-weight: bold; text-transform: uppercase; } +.cryptpad-toolbar-username { + line-height: 32px; +} +.cryptpad-toolbar-username button { + line-height: initial; +} .lag { height: 15px !important; width: 15px !important; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 6d415e2e6..f7f5a0294 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -210,6 +210,7 @@ define(function () { out.fc_newfolder = "Nouveau dossier"; out.fc_rename = "Renommer"; out.fc_open = "Ouvrir"; + out.fc_open_ro = "Ouvrir (lecture seule)"; out.fc_delete = "Supprimer"; out.fc_restore = "Restaurer"; out.fc_remove = "Supprimer définitivement"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 7e1ba420c..9cf23c8ef 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -212,6 +212,7 @@ define(function () { out.fc_newfolder = "New folder"; out.fc_rename = "Rename"; out.fc_open = "Open"; + out.fc_open_ro = "Open (read-only)"; out.fc_delete = "Delete"; out.fc_restore = "Restore"; out.fc_remove = "Delete permanently"; diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 260e7d22d..45dfa6c1f 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -288,7 +288,8 @@ define([ 'class': LAG_ELEM_CLS, id: uid(), }); - $container.prepend($lag); + // Now added in createUserAdmin + //$container.prepend($lag); return $lag[0]; }; @@ -356,7 +357,9 @@ define([ $linkContainer.append($aTagSmall).append($aTagBig); }; - var createUserAdmin = function ($topContainer) { + var createUserAdmin = function ($topContainer, lagElement) { + var $lag = $(lagElement); + var $userContainer = $('<span>', { 'class': USER_CLS }).appendTo($topContainer); @@ -365,6 +368,8 @@ define([ 'class': STATE_CLS }).text(Messages.synchronizing).appendTo($userContainer); + $userContainer.append($lag); + var $span = $('<span>' , { 'class': 'cryptpad-language' }); @@ -478,8 +483,8 @@ define([ var userListElement = config.userData ? createUserList(toolbar.find('.' + LEFTSIDE_CLS), readOnly) : undefined; var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad); var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS)); - var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS)); var lagElement = createLagElement($userAdminElement); + var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS), lagElement); var spinner = createSpinner($userAdminElement); var userData = config.userData; // readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode) diff --git a/www/drive/file.css b/www/drive/file.css index 96b02c9b0..7dd19d9ca 100644 --- a/www/drive/file.css +++ b/www/drive/file.css @@ -402,6 +402,8 @@ button.newElement:hover { } .dropdown-bar-content hr { margin: 5px 0px; + height: 1px; + background: #bbb; } /* Change color of dropdown links on hover */ diff --git a/www/drive/inner.html b/www/drive/inner.html index 85a5f2340..f1df46b3c 100644 --- a/www/drive/inner.html +++ b/www/drive/inner.html @@ -15,9 +15,10 @@ </div> <div id="content"> </div> - <div id="contextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> + <div id="treeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <li><a tabindex="-1" href="#" class="open" data-localization="fc_open">Open</a></li> + <li><a tabindex="-1" href="#" class="open_ro" data-localization="fc_open_ro">Open (read-only)</a></li> <li><a tabindex="-1" href="#" class="rename editable" data-localization="fc_rename">Rename</a></li> <li><a tabindex="-1" href="#" class="delete editable" data-localization="fc_delete">Delete</a></li> <li><a tabindex="-1" href="#" class="newfolder editable" data-localization="fc_newfolder">New folder</a></li> @@ -32,6 +33,12 @@ <li><a tabindex="-1" href="#" class="newdoc own editable" data-type="poll" data-localization="fc_newpoll" target="_blank">New poll</a></li> </ul> </div> + <div id="defaultContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> + <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> + <li><a tabindex="-1" href="#" class="open" data-localization="fc_open">Open</a></li> + <li><a tabindex="-1" href="#" class="open_ro" data-localization="fc_open_ro">Open (read-only)</a></li> + </ul> + </div> <div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <li><a tabindex="-1" href="#" class="empty editable" data-localization="fc_empty">Empty the trash</a></li> diff --git a/www/drive/main.js b/www/drive/main.js index a0fd2750a..fff4a74ff 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -130,20 +130,6 @@ define([ else { $iframe.find('[draggable="false"]').attr('draggable', true); } }; - var keyPressed = []; - var pressKey = function (key, state) { - if (state) { - if (keyPressed.indexOf(key) === -1) { - keyPressed.push(key); - } - return; - } - var idx = keyPressed.indexOf(key); - if (idx !== -1) { - keyPressed.splice(idx, 1); - } - }; - var init = function (files) { var isOwnDrive = function () { return Cryptpad.getUserHash() === APP.hash || localStorage.FS_hash === APP.hash; @@ -190,13 +176,17 @@ define([ // Store the object sent for the "change username" button so that we can update the field value correctly var userNameButtonObject = APP.userName = {}; /* add a "change username" button */ - getLastName(function (err, lastName) { - userNameButtonObject.lastName = lastName; - var $username = APP.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide(); - $userBlock.append($username); - $username.append(lastName); - $username.show(); - }); + if (!APP.readOnly) { + getLastName(function (err, lastName) { + userNameButtonObject.lastName = lastName; + var $username = APP.$userNameButton = Cryptpad.createButton('username', false, userNameButtonObject, setName).hide(); + $userBlock.append($username); + $username.append(lastName); + $username.show(); + }); + } else { + $userBlock.html('<span class="' + Toolbar.constants.readonly + '">' + Messages.readonly + '</span>'); + } // FILE MANAGER // _WORKGROUP_ and other people drive : display Documents as main page @@ -206,8 +196,9 @@ define([ var $tree = $iframe.find("#tree"); var $content = $iframe.find("#content"); - var $contextMenu = $iframe.find("#contextMenu"); + var $contextMenu = $iframe.find("#treeContextMenu"); var $contentContextMenu = $iframe.find("#contentContextMenu"); + var $defaultContextMenu = $iframe.find("#defaultContextMenu"); var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu"); var $trashContextMenu = $iframe.find("#trashContextMenu"); @@ -292,13 +283,13 @@ define([ window.open(fileEl); }; - var refresh = function () { + var refresh = APP.refresh = function () { module.displayDirectory(currentPath); }; // Replace a file/folder name by an input to change its value var displayRenameInput = function ($element, path) { - if (!APP.editable) { debug("Read-only mode"); return; } + if (!APP.editable) { return; } if (!path || path.length < 2) { logError("Renaming a top level element (root, trash or filesData) is forbidden."); return; @@ -345,9 +336,9 @@ define([ }; // Add the "selected" class to the "li" corresponding to the clicked element - var onElementClick = function ($element, path) { + var onElementClick = function (e, $element, path) { // If "Ctrl" is pressed, do not remove the current selection - if (keyPressed.indexOf(17) === -1) { + if (!e || !e.ctrlKey) { removeSelected(); } if (!$element.is('li')) { @@ -371,7 +362,7 @@ define([ e.stopPropagation(); var path = $(e.target).closest('li').data('path'); - if (!path) { return; } + if (!path) { return false; } if (!APP.editable) { $menu.find('a.editable').parent('li').hide(); @@ -394,12 +385,12 @@ define([ // $element should be the <span class="element">, find it if it's not the case var $element = $(e.target).closest('li').children('span.element'); - onElementClick($element); + onElementClick(undefined, $element); if (!$element.length) { logError("Unable to locate the .element tag", e.target); $menu.hide(); log(Messages.fm_contextMenuError); - return; + return false; } $menu.find('a').data('path', path); $menu.find('a').data('element', $element); @@ -411,11 +402,25 @@ define([ $contextMenu.find('li').show(); if ($element.find('.file-element').length) { $contextMenu.find('a.newfolder').parent('li').hide(); + } else { + $contextMenu.find('a.open_ro').parent('li').hide(); } openContextMenu(e, $contextMenu); return false; }; + var openDefaultContextMenu = function (e) { + var $element = $(e.target).closest('li'); + $defaultContextMenu.find('li').show(); + if ($element.find('.file-element').length) { + $defaultContextMenu.find('a.newfolder').parent('li').hide(); + } else { + $defaultContextMenu.find('a.open_ro').parent('li').hide(); + } + openContextMenu(e, $defaultContextMenu); + return false; + }; + var openTrashTreeContextMenu = function (e) { openContextMenu(e, $trashTreeContextMenu); return false; @@ -486,7 +491,7 @@ define([ // filesOp.moveElements is able to move several paths to a new location, including // the Trash or the "Unsorted files" folder var moveElements = function (paths, newPath, force, cb) { - if (!APP.editable) { debug("Read-only mode"); return; } + if (!APP.editable) { return; } var andThen = function () { filesOp.moveElements(paths, newPath, cb); }; @@ -577,7 +582,7 @@ define([ }; var addDragAndDropHandlers = function ($element, path, isFolder, droppable) { - if (!APP.editable) { debug("Read-only mode"); return; } + if (!APP.editable) { return; } // "dragenter" is fired for an element and all its children // "dragleave" may be fired when entering a child // --> we use pointer-events: none in CSS, but we still need a counter to avoid some issues @@ -701,7 +706,7 @@ define([ addDragAndDropHandlers($element, newPath, isFolder, !isTrash); $element.click(function(e) { e.stopPropagation(); - onElementClick($element, newPath); + onElementClick(e, $element, newPath); }); if (!isTrash) { $element.contextmenu(openDirectoryContextMenu); @@ -859,6 +864,7 @@ define([ var createNewButton = function (isInRoot) { var $block = $('<div>', {'class': 'dropdown-bar'}); + if (!APP.editable) { return $block; } var $button = $('<button>', { 'class': 'newElement' @@ -866,6 +872,11 @@ define([ var $innerblock = $('<div>', {'class': 'dropdown-bar-content'}); + if (isInRoot) { + $innerblock.append(createNewFolderButton()); + $innerblock.append('<hr>'); + } + AppConfig.availablePadTypes.forEach(function (type) { var $button = $('<a>', { 'class': 'newElement newdoc', @@ -877,11 +888,6 @@ define([ $innerblock.append($button); }); - if (isInRoot) { - $innerblock.append('<hr>'); - $innerblock.append(createNewFolderButton()); - } - $block.append($button).append($innerblock); $button.click(function (e) { @@ -1052,6 +1058,13 @@ define([ if (prop && !folder) { var element = el.element; var e = filesOp.getFileData(element); + if (!e) { + e = { + title : Messages.fm_noname, + atime : 0, + ctime : 0 + } + } if (prop === 'atime' || prop === 'ctime') { return new Date(e[prop]); } @@ -1106,8 +1119,9 @@ define([ $element.data('path', path); $element.click(function(e) { e.stopPropagation(); - onElementClick($element, path); + onElementClick(e, $element, path); }); + $element.contextmenu(openDefaultContextMenu); addDragAndDropHandlers($element, path, false, false); $container.append($element); }); @@ -1129,8 +1143,9 @@ define([ }); $element.click(function(e) { e.stopPropagation(); - onElementClick($element); + onElementClick(e, $element); }); + $element.contextmenu(openDefaultContextMenu); $container.append($element); }); }; @@ -1178,6 +1193,7 @@ define([ // NOTE: Elements in the trash are not using the same storage structure as the others // _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage var displayDirectory = module.displayDirectory = function (path, force) { + if (!APP.editable) { debug("Read-only mode"); } if (!appStatus.isReady && !force) { return; } // Only Trash and Root are available in not-owned files manager if (isWorkgroup() && !filesOp.isPathInTrash(path) && !filesOp.isPathInRoot(path)) { @@ -1424,6 +1440,7 @@ define([ $trashTreeContextMenu.hide(); $trashContextMenu.hide(); $contentContextMenu.hide(); + $defaultContextMenu.hide(); }; var stringifyPath = function (path) { @@ -1460,6 +1477,16 @@ define([ return $div.html(); }; + var getReadOnlyUrl = APP.getRO = function (href) { + if (!filesOp.isFile(href)) { return; } + var i = href.indexOf('#') + 1; + var hash = href.slice(i); + var base = href.slice(0, i); + var hrefsecret = Cryptpad.getSecrets(hash); + var viewHash = Cryptpad.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys); + return base + viewHash; + }; + $contextMenu.on("click", "a", function(e) { e.stopPropagation(); var path = $(this).data('path'); @@ -1478,6 +1505,12 @@ define([ else if ($(this).hasClass('open')) { $element.dblclick(); } + else if ($(this).hasClass('open_ro')) { + var el = filesOp.findElement(files, path); + if (filesOp.isFolder(el)) { return; } + var roUrl = getReadOnlyUrl(el); + openFile(roUrl); + } else if ($(this).hasClass('newfolder')) { var onCreated = function (info) { module.newFolder = info.newPath; @@ -1488,6 +1521,27 @@ define([ module.hideMenu(); }); + $defaultContextMenu.on("click", "a", function(e) { + e.stopPropagation(); + var path = $(this).data('path'); + var $element = $(this).data('element'); + if (!$element || !path || path.length < 2) { + log(Messages.fm_forbidden); + debug("Directory context menu on a forbidden or unexisting element. ", $element, path); + return; + } + if ($(this).hasClass('open')) { + $element.dblclick(); + } + else if ($(this).hasClass('open_ro')) { + var el = filesOp.findElement(files, path); + if (filesOp.isFolder(el)) { return; } + var roUrl = getReadOnlyUrl(el); + openFile(roUrl); + } + module.hideMenu(); + }); + $contentContextMenu.on('click', 'a', function (e) { e.stopPropagation(); var path = $(this).data('path'); @@ -1570,12 +1624,6 @@ define([ $(ifrw).on('mouseup drop', function (e) { $iframe.find('.droppable').removeClass('droppable'); }); - $(ifrw).on('keydown', function (e) { - pressKey(e.which, true); - }); - $(ifrw).on('keyup', function (e) { - pressKey(e.which, false); - }); $(ifrw).on('keydown', function (e) { // "Del" if (e.which === 46) { @@ -1761,10 +1809,13 @@ define([ }; var onDisconnect = function (info) { setEditable(false); + if (APP.refresh) { APP.refresh(); } + APP.toolbar.failed(); Cryptpad.alert(Messages.common_connectionLost); }; var onReconnect = function (info) { setEditable(true); + if (APP.refresh) { APP.refresh(); } APP.toolbar.reconnecting(info.myId); Cryptpad.findOKButton().click(); };