diff --git a/customize.dist/src/less/toolbar.less b/customize.dist/src/less/toolbar.less index 91afca6ae..cbbbd9dd2 100644 --- a/customize.dist/src/less/toolbar.less +++ b/customize.dist/src/less/toolbar.less @@ -56,7 +56,7 @@ display: flex; overflow: visible; iframe { - height: auto; + height: 100%; width: 100%; } } diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 5c9c3542b..af793c275 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -205,6 +205,13 @@ define([ var randomToken = function () { return Math.random().toString(16).replace(/0./, ''); }; + + common.isFeedbackAllowed = function () { + try { + if (!getStore().getProxy().proxy.allowUserFeedback) { return; } + return true; + } catch (e) { return void console.error(e); } + }; var feedback = common.feedback = function (action, force) { if (force !== true) { if (!action) { return; } diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index cfcf5722d..fa513c72e 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -101,10 +101,29 @@ define([ ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, cb); }; - // TODO + funcs.feedback = function (action, force) { + if (force !== true) { + if (!action) { return; } + try { + if (!ctx.metadataMgr.getPrivateData().feedbackAllowed) { return; } + } catch (e) { return void console.error(e); } + } + var randomToken = Math.random().toString(16).replace(/0./, ''); + var origin = ctx.metadataMgr.getPrivateData().origin; + var href = /*origin +*/ '/common/feedback.html?' + action + '=' + randomToken; + $.ajax({ + type: "HEAD", + url: href, + }); + }; + var prepareFeedback = function (key) { + if (typeof(key) !== 'string') { return $.noop; } - funcs.feedback = function () {}; - var prepareFeedback = function () {}; + var type = ctx.metadataMgr.getMetadata().type; + return function () { + funcs.feedback((key + (type? '_' + type: '')).toUpperCase()); + }; + }; // BUTTONS var isStrongestStored = function () { diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 6877d6593..e254dbb55 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -161,6 +161,7 @@ define([ var metadataMgr = config.metadataMgr; var userData = metadataMgr.getMetadata().users; var viewers = metadataMgr.getViewers(); + var origin = config.metadataMgr.getPrivateData().origin; // If we are using old pads (readonly unavailable), only editing users are in userList. // With new pads, we also have readonly users in userList, so we have to intersect with @@ -225,7 +226,7 @@ define([ if (data.profile) { $span.addClass('clickable'); $span.click(function () { - window.open('/profile/#' + data.profile); + window.open(origin+'/profile/#' + data.profile); }); } if (data.avatar && avatars[data.avatar]) { diff --git a/www/common/userObject.js b/www/common/userObject.js index 6b4a31bbe..431e9c9dc 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -413,6 +413,15 @@ define([ }); return ret; }; + exp.getRecentPads = function () { + var allFiles = files[FILES_DATA]; + var sorted = Object.keys(allFiles) + .sort(function (a,b) { + return allFiles[a].atime < allFiles[b].atime; + }) + .map(function (str) { return Number(str); }); + return sorted; + }; /** * OPERATIONS diff --git a/www/drive/file.less b/www/drive/file.less index ccf92651f..41896d755 100644 --- a/www/drive/file.less +++ b/www/drive/file.less @@ -190,7 +190,8 @@ span { } .docTree { margin-top: 20px; - padding: 0 0 0 20px; + //padding: 0 0 0 20px; + padding: 0; cursor: auto; &li, li { padding: 0; @@ -304,6 +305,24 @@ span { top: -1px; } } + .docTree { + .root > .element-row > .expcol { + position: relative; + top:0; + left: -10px; + } + .root > .element-row > .folder { + margin-left: -5px; + } + .root { + &> .element-row { + padding-left: 20px; + } + &> ul { + padding-left: 30px; + } + } + } // Expand/collapse lines .docTree ul { @@ -478,9 +497,26 @@ span { .listElement { display: none; } + .addpad { + cursor: pointer; + opacity: 0.5; + padding: 0; + &:hover { + opacity: 0.7; + } + .fa { + cursor: pointer; + font-size: 100px; + line-height: 140px; + margin: 0; + } + } } .list { + .grid-element { + display: none; + } // Make it act as a table! padding-left: 20px; ul { diff --git a/www/drive/main.js b/www/drive/main.js index 4ecdbc5c1..02bfeb91f 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -51,6 +51,8 @@ define([ var TEMPLATE_NAME = Messages.fm_templateName; var TRASH = "trash"; var TRASH_NAME = Messages.fm_trashName; + var RECENT = "recent"; + var RECENT_NAME = "Recent pads"; var LOCALSTORAGE_LAST = "cryptpad-file-lastOpened"; var LOCALSTORAGE_OPENED = "cryptpad-file-openedFolders"; @@ -172,6 +174,7 @@ define([ var $closeIcon = $('', {"class": "fa fa-window-close"}); var $backupIcon = $('', {"class": "fa fa-life-ring"}); var $searchIcon = $('', {"class": "fa fa-search searchIcon"}); + var $addIcon = $('', {"class": "fa fa-plus"}); var history = { isHistoryMode: false, @@ -233,9 +236,10 @@ define([ // Categories dislayed in the menu // _WORKGROUP_ : do not display unsorted - var displayedCategories = [ROOT, TRASH, SEARCH]; + var displayedCategories = [ROOT, TRASH, SEARCH, RECENT]; if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); } if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; } + var virtualCategories = [SEARCH, RECENT]; if (!APP.loggedIn) { displayedCategories = [FILES_DATA]; @@ -1259,6 +1263,7 @@ define([ case TEMPLATE: pName = TEMPLATE_NAME; break; case FILES_DATA: pName = FILES_DATA_NAME; break; case SEARCH: pName = SEARCH_NAME; break; + case RECENT: pName = RECENT_NAME; break; default: pName = name; } return pName; @@ -1318,6 +1323,9 @@ define([ case FILES_DATA: msg = Messages.fm_info_allFiles; break; + case RECENT: + msg = Messages.fm_info_recent || 'TODO'; + break; default: msg = undefined; } @@ -1805,6 +1813,50 @@ define([ }); }; + var displayRecent = function ($list) { + var filesList = filesOp.getRecentPads(); + var limit = 20; + var i = 0; + filesList.forEach(function (id) { + if (i >= 20) { return; } + // Check path (pad exists and not in trash) + var paths = filesOp.findFile(id); + if (!paths.length) { return; } + var path = paths[0]; + if (filesOp.isPathIn(path, [TRASH])) { return; } + // Display the pad + var file = filesOp.getFileData(id); + if (!file) { + //debug("Unsorted or template returns an element not present in filesData: ", href); + file = { title: Messages.fm_noname }; + //return; + } + var $icon = getFileIcon(id); + var ro = filesOp.isReadOnlyFile(id); + // ro undefined mens it's an old hash which doesn't support read-only + var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : ''; + var $element = $('
  • ', { + 'class': 'file-element element element-row' + roClass, + }); + addFileData(id, $element); + $element.prepend($icon).dblclick(function () { + openFile(id); + }); + $element.data('path', path); + $element.click(function(e) { + e.stopPropagation(); + onElementClick(e, $element, path); + }); + $element.contextmenu(openDefaultContextMenu); + $element.data('context', $defaultContextMenu); + /*if (draggable) { + addDragAndDropHandlers($element, path, false, false); + }*/ + $list.append($element); + i++; + }); + }; + // Display the selected directory into the content part (rightside) // NOTE: Elements in the trash are not using the same storage structure as the others // _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage @@ -1833,9 +1885,11 @@ define([ var isTemplate = filesOp.comparePath(path, [TEMPLATE]); var isAllFiles = filesOp.comparePath(path, [FILES_DATA]); var isSearch = path[0] === SEARCH; + var isRecent = path[0] === RECENT; + var isVirtual = virtualCategories.indexOf(path[0]) !== -1; - var root = isSearch ? undefined : filesOp.find(path); - if (!isSearch && typeof(root) === "undefined") { + var root = isVirtual ? undefined : filesOp.find(path); + if (!isVirtual && typeof(root) === "undefined") { log(Messages.fm_unknownFolderError); debug("Unable to locate the selected directory: ", path); var parentPath = path.slice(); @@ -1921,6 +1975,8 @@ define([ displayTrashRoot($list, $folderHeader, $fileHeader); } else if (isSearch) { displaySearch($list, path[1]); + } else if (isRecent) { + displayRecent($list); } else { $dirContent.contextmenu(openContentContextMenu); if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); } @@ -1940,6 +1996,21 @@ define([ var $element = createElement(path, key, root, false); $element.appendTo($list); }); + + var $element = $('
  • ', { + 'class': 'element-row grid-element addpad' + }).prepend($addIcon.clone()).appendTo($list); + $element.attr('title', "TODO: Add a pad"); + $element.click(function () { + window.setTimeout(function () { + $driveToolbar.find('.leftside .dropdown-bar-content').show(); + }); + /*var $content = $driveToolbar.find('.leftside .dropdown-bar-content').html(); + var $container = $('
    ').append($('
    ', {id:'cryptpad-add-pad'})); + Cryptpad.alert($container.html(),null,true); + var $c = $('body > .alertify').last().find('#cryptpad-add-pad'); + $c.append($content);*/ + }); } //$content.append($toolbar).append($title).append($info).append($dirContent); $content.append($info).append($dirContent); @@ -2037,7 +2108,10 @@ define([ var $rootIcon = filesOp.isFolderEmpty(files[ROOT]) ? (isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon) : (isRootOpened ? $folderOpenedIcon : $folderIcon); - var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), [ROOT], false, true, false, isRootOpened); + var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), [ROOT], false, true, true, isRootOpened); + if (!filesOp.hasSubfolder(root)) { + $rootElement.find('.expcol').css('visibility', 'hidden'); + } $rootElement.addClass('root'); $rootElement.find('>.element-row').contextmenu(openDirectoryContextMenu); $('
      ', {'class': 'docTree'}).append($rootElement).appendTo($container); @@ -2093,6 +2167,15 @@ define([ $container.append($trashList); }; + var createRecent = function ($container, path) { + var $icon = $templateIcon.clone(); //TODO + var isOpened = filesOp.comparePath(path, currentPath); + var $element = createTreeElement(RECENT_NAME, $icon, [RECENT], false, false, false, isOpened); + $element.addClass('root'); + var $list = $('
        ', { id: 'recentTree', 'class': 'category' }).append($element); + $container.append($list); + }; + var search = APP.Search = {}; var createSearch = function ($container) { var isInSearch = currentPath[0] === SEARCH; @@ -2145,6 +2228,7 @@ define([ $tree.html(''); if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); } var $div = $('
        ', {'class': 'categories-container'}).appendTo($tree); + if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); } if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); } if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); } if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); } diff --git a/www/pad2/main.js b/www/pad2/main.js index c56d58629..f0e1e063b 100644 --- a/www/pad2/main.js +++ b/www/pad2/main.js @@ -528,11 +528,11 @@ define([ $collapse.removeClass('fa-caret-down').removeClass('fa-caret-up'); var isCollapsed = !$bar.find('.cke_toolbox_main').is(':visible'); if (isCollapsed) { - if (!initializing) { Cryptpad.feedback('HIDETOOLBAR_PAD'); } + if (!initializing) { common.feedback('HIDETOOLBAR_PAD'); } $collapse.addClass('fa-caret-down'); } else { - if (!initializing) { Cryptpad.feedback('SHOWTOOLBAR_PAD'); } + if (!initializing) { common.feedback('SHOWTOOLBAR_PAD'); } $collapse.addClass('fa-caret-up'); } }; @@ -721,7 +721,7 @@ define([ var id = classes[0]; if (typeof(id) === 'string') { - Cryptpad.feedback(id.toUpperCase()); + common.feedback(id.toUpperCase()); } }); }; diff --git a/www/pad2/outer.js b/www/pad2/outer.js index f2a2d6855..5f603eab6 100644 --- a/www/pad2/outer.js +++ b/www/pad2/outer.js @@ -77,7 +77,8 @@ define([ pathname: window.location.pathname, readOnly: readOnly, availableHashes: hashes, - isTemplate: Cryptpad.isTemplate(window.location.href) + isTemplate: Cryptpad.isTemplate(window.location.href), + feedbackAllowed: Cryptpad.isFeedbackAllowed() } }); });