diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 3764328a5d..800873cd63 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -8,7 +8,9 @@ * */ -OCA.Sharing = {}; +if (!OCA.Sharing) { + OCA.Sharing = {}; +} OCA.Sharing.App = { _inFileList: null, diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 5a42604c86..db770ad150 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -8,103 +8,154 @@ * */ -$(document).ready(function() { - if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) { - // TODO: make a separate class for this or a hook or jQuery event ? - if (OCA.Files.FileList) { - var oldCreateRow = OCA.Files.FileList.prototype._createRow; - OCA.Files.FileList.prototype._createRow = function(fileData) { - var tr = oldCreateRow.apply(this, arguments); - if (fileData.shareOwner) { - tr.attr('data-share-owner', fileData.shareOwner); - // user should always be able to rename a mount point - if (fileData.isShareMountPoint) { - tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); - tr.attr('data-reshare-permissions', fileData.permissions); - } +(function() { + if (!OCA.Sharing) { + OCA.Sharing = {}; + } + OCA.Sharing.Util = { + initialize: function() { + if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) { + // TODO: make a separate class for this or a hook or jQuery event ? + if (OCA.Files.FileList) { + var oldCreateRow = OCA.Files.FileList.prototype._createRow; + OCA.Files.FileList.prototype._createRow = function(fileData) { + var tr = oldCreateRow.apply(this, arguments); + if (fileData.shareOwner) { + tr.attr('data-share-owner', fileData.shareOwner); + // user should always be able to rename a mount point + if (fileData.isShareMountPoint) { + tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); + tr.attr('data-reshare-permissions', fileData.permissions); + } + } + return tr; + }; } - return tr; - }; - } - // use delegate to catch the case with multiple file lists - $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ - // if no share action exists because the admin disabled sharing for this user - // we create a share notification action to inform the user about files - // shared with him otherwise we just update the existing share action. - var fileList = ev.fileList; - var $fileList = $(this); - $fileList.find('[data-share-owner]').each(function() { - var $tr = $(this); - var $action; - var owner; - var message; - var permissions = $tr.data('permissions'); - if(permissions & OC.PERMISSION_SHARE) { - $action = $tr.find('[data-Action="Share"]'); - $action.addClass('permanent'); - owner = $tr.closest('tr').attr('data-share-owner'); - message = ' ' + t('files_sharing', 'Shared by {owner}', {owner: owner}); - $action.find('span').text(message); - } else { - var shareNotification = '' + - ' '; - $tr.find('.fileactions').append(function() { - var owner = $(this).closest('tr').attr('data-share-owner'); - var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); - var $result = $(shareNotification + ' ' + shareBy + ''); - $result.on('click', function() { - return false; - }); - return $result; + // use delegate to catch the case with multiple file lists + $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ + // if no share action exists because the admin disabled sharing for this user + // we create a share notification action to inform the user about files + // shared with him otherwise we just update the existing share action. + var fileList = ev.fileList; + var $fileList = $(this); + $fileList.find('[data-share-owner]').each(function() { + var $tr = $(this); + var permissions = $tr.data('permissions'); + if(permissions & OC.PERMISSION_SHARE) { + OC.Share.markFileAsShared($tr, true); + } else { + // TODO: make this work like/with OC.Share.markFileAsShared() + var shareNotification = '' + + ' '; + $tr.find('.fileactions').append(function() { + var owner = $(this).closest('tr').attr('data-share-owner'); + var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); + var $result = $(shareNotification + ' ' + shareBy + ''); + $result.on('click', function() { + return false; + }); + return $result; + }); + } }); - } - }); - if (!OCA.Sharing.sharesLoaded){ - OC.Share.loadIcons('file', fileList); - // assume that we got all shares, so switching directories - // will not invalidate that list - OCA.Sharing.sharesLoaded = true; - } - else{ - OC.Share.updateIcons('file', fileList); - } - }); + if (!OCA.Sharing.sharesLoaded){ + OC.Share.loadIcons('file', fileList); + // assume that we got all shares, so switching directories + // will not invalidate that list + OCA.Sharing.sharesLoaded = true; + } + else{ + OC.Share.updateIcons('file', fileList); + } + }); - OCA.Files.fileActions.register( - 'all', - 'Share', - OC.PERMISSION_SHARE, - OC.imagePath('core', 'actions/share'), - function(filename, context) { + OCA.Files.fileActions.register( + 'all', + 'Share', + OC.PERMISSION_SHARE, + OC.imagePath('core', 'actions/share'), + function(filename, context) { - var $tr = context.$file; - var itemType = 'file'; - if ($tr.data('type') === 'dir') { - itemType = 'folder'; - } - var possiblePermissions = $tr.data('reshare-permissions'); - if (_.isUndefined(possiblePermissions)) { - possiblePermissions = $tr.data('permissions'); - } + var $tr = context.$file; + var itemType = 'file'; + if ($tr.data('type') === 'dir') { + itemType = 'folder'; + } + var possiblePermissions = $tr.data('reshare-permissions'); + if (_.isUndefined(possiblePermissions)) { + possiblePermissions = $tr.data('permissions'); + } - var appendTo = $tr.find('td.filename'); - // Check if drop down is already visible for a different file - if (OC.Share.droppedDown) { - if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) { - OC.Share.hideDropDown(function () { + var appendTo = $tr.find('td.filename'); + // Check if drop down is already visible for a different file + if (OC.Share.droppedDown) { + if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) { + OC.Share.hideDropDown(function () { + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + }); + } else { + OC.Share.hideDropDown(); + } + } else { $tr.addClass('mouseOver'); OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + } + $('#dropdown').on('sharesChanged', function(ev) { + // note: we only update the data attribute because updateIcon() + // is called automatically after this event + var userShares = ev.itemShares[OC.Share.SHARE_TYPE_USER] || []; + var groupShares = ev.itemShares[OC.Share.SHARE_TYPE_GROUP] || []; + var linkShares = ev.itemShares[OC.Share.SHARE_TYPE_LINK] || []; + var recipients = _.union(userShares, groupShares); + if (linkShares.length > 0) { + recipients.unshift(t('files_sharing', 'Public')); + } + // only update the recipients if they existed before + // (some file lists don't have them) + if (!_.isUndefined($tr.attr('data-share-recipients'))) { + // FIXME: use display names from users, we currently only got user ids + if (recipients.length) { + $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); + } + else { + $tr.attr('data-share-recipients', ''); + } + } }); - } else { - OC.Share.hideDropDown(); - } - } else { - $tr.addClass('mouseOver'); - OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + }); } - }); - } + }, + + /** + * Formats a recipient array to be displayed. + * The first four recipients will be shown and the + * other ones will be shown as "+x" where "x" is the number of + * remaining recipients. + * + * @param recipients recipients array + * @param count optional total recipients count (in case the array was shortened) + * @return formatted recipients display text + */ + formatRecipients: function(recipients, count) { + var maxRecipients = 4; + var text; + if (!_.isNumber(count)) { + count = recipients.length; + } + text = _.first(recipients, maxRecipients).join(', '); + if (count > maxRecipients) { + text += ', +' + (count - maxRecipients); + } + return text; + } + }; +})(); + +$(document).ready(function() { + OCA.Sharing.Util.initialize(); }); + diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index ef1034ecfd..9cf74abf67 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -48,6 +48,9 @@ if (this._sharedWithUser) { $tr.attr('data-share-owner', fileData.shares[0].ownerDisplayName); } + if (fileData.recipientsDisplayName) { + $tr.attr('data-share-recipients', fileData.recipientsDisplayName); + } return $tr; }, @@ -179,15 +182,15 @@ // inside the same file object (by file id). .reduce(function(memo, file) { var data = memo[file.id]; - var counterPart = file.share.ownerDisplayName || file.share.targetDisplayName; + var recipient = file.share.targetDisplayName; if (!data) { data = memo[file.id] = file; data.shares = [file.share]; // using a hash to make them unique, // this is only a list to be displayed - data.counterParts = {}; + data.recipients = {}; // counter is cheaper than calling _.keys().length - data.counterPartsCount = 0; + data.recipientsCount = 0; data.mtime = file.share.stime; } else { @@ -200,10 +203,14 @@ if (file.share.type === OC.Share.SHARE_TYPE_LINK) { data.hasLinkShare = true; - } else if (counterPart && data.counterPartsCount < 10) { + } else if (recipient) { // limit counterparts for output - data.counterParts[counterPart] = true; - data.counterPartsCount++; + if (data.recipientsCount < 3) { + // only store the first ones, they will be the only ones + // displayed + data.recipients[recipient] = true; + } + data.recipientsCount++; } delete file.share; @@ -213,14 +220,18 @@ .values() // Clean up .each(function(data) { - // convert the counterParts map to a flat + // convert the recipients map to a flat // array of sorted names - data.counterParts = _.chain(data.counterParts).keys().sort().value(); + data.recipients = _.chain(data.recipients).keys().sort().value(); if (data.hasLinkShare) { - data.counterParts.unshift(t('files_sharing', 'link')); + data.recipients.unshift(t('files_sharing', 'Public')); delete data.hasLinkShare; } - delete data.counterPartsCount; + data.recipientsDisplayName = OCA.Sharing.Util.formatRecipients( + data.recipients, + data.recipientsCount + ); + delete data.recipientsCount; }) // Sort by expected sort comparator .sortBy(this._sortComparator) diff --git a/core/js/share.js b/core/js/share.js index 0c6d39e446..d802053176 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -56,20 +56,14 @@ OC.Share={ image = OC.imagePath('core', 'actions/public'); } if (itemType !== 'file' && itemType !== 'folder') { - $fileList.find('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); + $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); } else { + // TODO: ultimately this part should be moved to files_sharing app var file = $fileList.find('tr[data-id="'+item+'"]'); var shareFolder = OC.imagePath('core', 'filetypes/folder-shared'); var img; if (file.length > 0) { - var type = file.data('type'); - if (type === 'dir') { - file.children('.filename').css('background-image', 'url('+shareFolder+')'); - } - var action = $(file).find('.fileactions .action[data-action="Share"]'); - img = action.find('img').attr('src', image); - action.addClass('permanent'); - action.html(' '+t('core', 'Shared')+'').prepend(img); + this.markFileAsShared(file, true, image); } else { var dir = currentDir; if (dir.length > 1) { @@ -82,6 +76,7 @@ OC.Share={ var files = $fileList.find('.filename'); var i; for (i = 0; i < actions.length; i++) { + // TODO: use this.markFileAsShared() img = $(actions[i]).find('img'); if (img.attr('src') !== OC.imagePath('core', 'actions/public')) { img.attr('src', image); @@ -125,29 +120,9 @@ OC.Share={ if (itemType != 'file' && itemType != 'folder') { $('a.share[data-item="'+itemSource+'"]').css('background', 'url('+image+') no-repeat center'); } else { - var file = $('tr').filterAttr('data-id', String(itemSource)); - if (file.length > 0) { - var type = file.data('type'); - var shareFolder = OC.imagePath('core', 'filetypes/folder'); - if (type === 'dir' && shares) { - shareFolder = OC.imagePath('core', 'filetypes/folder-shared'); - file.children('.filename').css('background-image', 'url('+shareFolder+')'); - } else if (type === 'dir') { - file.children('.filename').css('background-image', 'url('+shareFolder+')'); - } - var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); - // in case of multiple lists/rows, there might be more than one visible - action.each(function() { - var action = $(this); - var img = action.find('img').attr('src', image); - if (shares) { - action.addClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Shared'))+'').prepend(img); - } else { - action.removeClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Share'))+'').prepend(img); - } - }); + var $tr = $('tr').filterAttr('data-id', String(itemSource)); + if ($tr.length > 0) { + this.markFileAsShared($tr, shares, image); } } if (shares) { @@ -157,6 +132,59 @@ OC.Share={ delete OC.Share.statuses[itemSource]; } }, + /** + * Marks/unmarks a given file as shared + * + * @param $tr file element to mark as shared + * @param state true to mark as shared, false to unmark + * @param image image to use for the icon + */ + markFileAsShared: function($tr, state, image) { + var action = $tr.find('.fileactions .action[data-action="Share"]'); + var type = $tr.data('type'); + var img = action.find('img'); + var message; + var recipients; + var owner; + var shareFolderIcon; + if (type === 'dir' && state) { + shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared'); + $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')'); + } else if (type === 'dir') { + shareFolderIcon = OC.imagePath('core', 'filetypes/folder'); + $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')'); + } + if (state) { + recipients = $tr.attr('data-share-recipients'); + owner = $tr.attr('data-share-owner'); + + action.addClass('permanent'); + message = t('core', 'Shared'); + if (owner && !recipients) { + message = t('files_sharing', 'Shared by {owner}', {owner: owner}); + image = image || OC.imagePath('core', 'actions/share'); + } + if (recipients) { + image = image || OC.imagePath('core', 'actions/shared'); + if (owner) { + message = ' ' + t( + 'files_sharing', + 'Shared by {owner} with You, {recipients}', + {owner: owner, recipients: recipients} + ); + } + else { + message = t('core', 'Shared with {recipients}', {recipients: recipients}); + } + } + action.html(' '+ message + '').prepend(img); + } + else { + action.removeClass('permanent'); + action.html(' '+ escapeHTML(t('core', 'Share'))+'').prepend(img); + } + img.attr('src', image); + }, loadItem:function(itemType, itemSource) { var data = ''; var checkReshare = true; @@ -384,6 +412,7 @@ OC.Share={ OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, function() { OC.Share.addShareWith(shareType, shareWith, selected.item.label, permissions, possiblePermissions); $('#shareWith').val(''); + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); OC.Share.updateIcon(itemType, itemSource); }); return false; @@ -665,6 +694,7 @@ $(document).ready(function() { $li.remove(); var index = OC.Share.itemShares[shareType].indexOf(shareWith); OC.Share.itemShares[shareType].splice(index, 1); + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { $('#expiration').hide('blind'); @@ -723,6 +753,7 @@ $(document).ready(function() { if (oc_appconfig.core.enforcePasswordForPublicLink === false) { OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', OC.PERMISSION_READ, itemSourceName, expirationDate, function(data) { OC.Share.showLink(data.token, null, itemSource); + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); OC.Share.updateIcon(itemType, itemSource); }); } else {