diff options
-rw-r--r-- | .drone.yml | 22 | ||||
m--------- | 3rdparty | 0 | ||||
-rw-r--r-- | apps/comments/css/comments.css | 11 | ||||
-rw-r--r-- | apps/comments/js/commentstabview.js | 34 | ||||
-rw-r--r-- | apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php | 4 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 5 | ||||
-rw-r--r-- | apps/files_sharing/js/share.js | 50 | ||||
-rw-r--r-- | apps/files_sharing/js/sharedfilelist.js | 47 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/shareSpec.js | 89 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/sharedfilelistSpec.js | 4 | ||||
-rwxr-xr-x | autotest.sh | 4 | ||||
-rw-r--r-- | core/js/share.js | 44 | ||||
-rw-r--r-- | core/js/tests/specs/shareSpec.js | 147 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/DB/ConnectionFactory.php | 3 | ||||
-rw-r--r-- | lib/private/DB/OCPostgreSqlPlatform.php | 95 | ||||
-rw-r--r-- | tests/lib/DB/OCPostgreSqlPlatformTest.php | 35 |
18 files changed, 320 insertions, 276 deletions
diff --git a/.drone.yml b/.drone.yml index 2f2867a5b2b..186969ccd06 100644 --- a/.drone.yml +++ b/.drone.yml @@ -299,6 +299,7 @@ pipeline: matrix: DB: postgres PHP: 5.6 + POSTGRES: 9 postgres-php7.0: image: nextcloudci/php7.0:php7.0-16 commands: @@ -746,12 +747,19 @@ matrix: ENABLE_REDIS: true - DB: postgres PHP: 5.6 + POSTGRES: 9 ENABLE_REDIS: true - DB: postgres PHP: 7.0 + POSTGRES: 9 ENABLE_REDIS: true - DB: postgres PHP: 7.1 + POSTGRES: 9 + ENABLE_REDIS: true + - DB: postgres + PHP: 7.1 + POSTGRES: 10 ENABLE_REDIS: true - DB: mysqlmb4 PHP: 5.6 @@ -833,7 +841,7 @@ services: when: matrix: ENABLE_REDIS_CLUSTER: true - postgres: + postgres-9: image: postgres:9 environment: - POSTGRES_USER=oc_autotest @@ -843,6 +851,18 @@ services: when: matrix: DB: postgres + POSTGRES: 9 + postgres-10: + image: postgres:10 + environment: + - POSTGRES_USER=oc_autotest + - POSTGRES_PASSWORD=owncloud + tmpfs: + - /var/lib/postgresql/data + when: + matrix: + DB: postgres + POSTGRES: 10 mysql: image: mysql:5.7 environment: diff --git a/3rdparty b/3rdparty -Subproject ec71700daf6aab080502f67ecfca274dcb0bb3d +Subproject f4e328bc4cc67011d206ca024483531a3b8c544 diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index ccfad1d39f1..646f5f1100c 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -140,6 +140,17 @@ .atwho-view-ul * .avatar-name-wrapper, #commentsTabView .comment .authorRow { position: relative; +} + +#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser), +#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser) .avatar, +#commentsTabView .comment .authorRow .avatar:not(.currentUser), +#commentsTabView .comment .authorRow .author:not(.currentUser) { + cursor: pointer; +} + +.atwho-view-ul .avatar-name-wrapper, +.atwho-view-ul .avatar-name-wrapper .avatar { cursor: pointer; display: inline-flex; align-items: center; diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 5767ecfc9da..0c43e156985 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -23,8 +23,8 @@ var EDIT_COMMENT_TEMPLATE = '<div class="newCommentRow comment" data-id="{{id}}">' + ' <div class="authorRow">' + - ' <div class="avatar" data-username="{{actorId}}"></div>' + - ' <div class="author">{{actorDisplayName}}</div>' + + ' <div class="avatar currentUser" data-username="{{actorId}}"></div>' + + ' <div class="author currentUser">{{actorDisplayName}}</div>' + '{{#if isEditMode}}' + ' <a href="#" class="action delete icon icon-delete has-tooltip" title="{{deleteTooltip}}"></a>' + ' <div class="deleteLoading icon-loading-small hidden"></div>'+ @@ -43,8 +43,8 @@ var COMMENT_TEMPLATE = '<li class="comment{{#if isUnread}} unread{{/if}}{{#if isLong}} collapsed{{/if}}" data-id="{{id}}">' + ' <div class="authorRow">' + - ' <div class="avatar" {{#if actorId}}data-username="{{actorId}}"{{/if}}> </div>' + - ' <div class="author">{{actorDisplayName}}</div>' + + ' <div class="avatar{{#if isUserAuthor}} currentUser{{/if}}" {{#if actorId}}data-username="{{actorId}}"{{/if}}> </div>' + + ' <div class="author{{#if isUserAuthor}} currentUser{{/if}}">{{actorDisplayName}}</div>' + '{{#if isUserAuthor}}' + ' <a href="#" class="action edit icon icon-rename has-tooltip" title="{{editTooltip}}"></a>' + '{{/if}}' + @@ -215,13 +215,15 @@ searchKey: "label" }); $target.on('inserted.atwho', function (je, $el) { + var editionMode = true; s._postRenderItem( // we need to pass the parent of the inserted element // passing the whole comments form would re-apply and request // avatars from the server $(je.target).find( 'div[data-username="' + $el.find('[data-username]').data('username') + '"]' - ).parent() + ).parent(), + editionMode ); }); }, @@ -378,7 +380,7 @@ }); }, - _postRenderItem: function($el) { + _postRenderItem: function($el, editionMode) { $el.find('.has-tooltip').tooltip(); $el.find('.avatar').each(function() { var $this = $(this); @@ -396,16 +398,23 @@ // it is the case when writing a comment and mentioning a person $message = $el; } - this._postRenderMessage($message); + this._postRenderMessage($message, editionMode); }, - _postRenderMessage: function($el) { + _postRenderMessage: function($el, editionMode) { + if (editionMode) { + return; + } + $el.find('.avatar').each(function() { var avatar = $(this); var strong = $(this).next(); var appendTo = $(this).parent(); - $.merge(avatar, strong).contactsMenu(avatar.data('user'), 0, appendTo); + var username = $(this).data('username'); + if (username !== oc_current_user) { + $.merge(avatar, strong).contactsMenu(avatar.data('user'), 0, appendTo); + } }); }, @@ -446,9 +455,11 @@ + ' data-user-display-name="' + _.escape(displayName) + '"></div>'; + var isCurrentUser = (uid === OC.getCurrentUser().uid); + return '' + '<span class="atwho-inserted" contenteditable="false">' - + '<span class="avatar-name-wrapper">' + + '<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">' + avatar + ' <strong>'+ _.escape(displayName)+'</strong>' + '</span>' + '</span>'; @@ -487,7 +498,8 @@ .html(this._formatMessage(commentToEdit.get('message'), commentToEdit.get('mentions'))) .find('.avatar') .each(function () { $(this).avatar(); }); - this._postRenderItem($message); + var editionMode = true; + this._postRenderItem($message, editionMode); // Enable autosize autosize($formRow.find('.message')); diff --git a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php index b66cad7e8ed..68c9a1b415f 100644 --- a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php +++ b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php @@ -33,6 +33,7 @@ use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\InvalidSyncToken; use Sabre\DAV\Exception\NotAuthenticated; use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Exception\NotImplemented; use Sabre\DAV\Exception\PreconditionFailed; use Sabre\DAV\Exception\ServiceUnavailable; @@ -57,6 +58,9 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { // Happens when an external storage or federated share is temporarily // not available StorageNotAvailableException::class => true, + // happens if some a client uses the wrong method for a given URL + // the error message itself is visible on the client side anyways + NotImplemented::class => true, ]; /** @var string */ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 6996e423776..10efa54496a 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -553,9 +553,6 @@ actionsWidth += $(action).outerWidth(); }); - // subtract app navigation toggle when visible - containerWidth -= $('#app-navigation-toggle').width(); - this.breadcrumb._resize(); this.$table.find('>thead').width($('#app-content').width() - OC.Util.getScrollBarWidth()); @@ -1369,7 +1366,7 @@ * @return new tr element (not appended to the table) */ add: function(fileData, options) { - var index = -1; + var index; var $tr; var $rows; var $insertionPoint; diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 5cd04ece446..aa0803c491b 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -46,13 +46,14 @@ tr.attr('data-share-permissions', sharePermissions); if (fileData.shareOwner) { tr.attr('data-share-owner', fileData.shareOwner); + tr.attr('data-share-owner-id', fileData.shareOwnerId); // user should always be able to rename a mount point if (fileData.mountType === 'shared-root') { tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); } } - if (fileData.recipientsDisplayName) { - tr.attr('data-share-recipients', fileData.recipientsDisplayName); + if (fileData.recipientData && !_.isEmpty(fileData.recipientData)) { + tr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData)); } if (fileData.shareTypes) { tr.attr('data-share-types', fileData.shareTypes.join(',')); @@ -67,8 +68,7 @@ fileInfo.shareOwner = $el.attr('data-share-owner') || undefined; if( $el.attr('data-share-types')){ - var shareTypes = $el.attr('data-share-types').split(','); - fileInfo.shareTypes = shareTypes; + fileInfo.shareTypes = $el.attr('data-share-types').split(','); } if( $el.attr('data-expiration')){ @@ -77,8 +77,6 @@ fileInfo.shares.push({expiration: expirationTimestamp}); } - fileInfo.recipientsDisplayName = $el.attr('data-share-recipients') || undefined; - return fileInfo; }; @@ -218,10 +216,13 @@ var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname'); // note: we only update the data attribute because updateIcon() if (recipients.length) { - $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); + var recipientData = _.mapObject(shareModel.get('shares'), function (share) { + return {shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname}; + }); + $tr.attr('data-share-recipient-data', JSON.stringify(recipientData)); } else { - $tr.removeAttr('data-share-recipients'); + $tr.removeAttr('data-share-recipient-data'); } }, @@ -229,15 +230,15 @@ * Update the file action share icon for the given file * * @param $tr file element of the file to update - * @param {bool} hasUserShares true if a user share exists - * @param {bool} hasLinkShare true if a link share exists + * @param {boolean} hasUserShares true if a user share exists + * @param {boolean} hasLinkShare true if a link share exists * - * @return {bool} true if the icon was set, false otherwise + * @return {boolean} true if the icon was set, false otherwise */ _updateFileActionIcon: function($tr, hasUserShares, hasLinkShare) { // if the statuses are loaded already, use them for the icon // (needed when scrolling to the next page) - if (hasUserShares || hasLinkShare || $tr.attr('data-share-recipients') || $tr.attr('data-share-owner')) { + if (hasUserShares || hasLinkShare || $tr.attr('data-share-recipient-data') || $tr.attr('data-share-owner')) { OC.Share.markFileAsShared($tr, true, hasLinkShare); return true; } @@ -245,31 +246,6 @@ }, /** - * Formats a recipients 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 {Array.<String>} recipients recipients array - * @param {int} count optional total recipients count (in case the array was shortened) - * @return {String} formatted recipients display text - */ - formatRecipients: function(recipients, count) { - var maxRecipients = 4; - var text; - if (!_.isNumber(count)) { - count = recipients.length; - } - // TODO: use natural sort - recipients = _.first(recipients, maxRecipients).sort(); - text = recipients.join(', '); - if (count > maxRecipients) { - text += ', +' + (count - maxRecipients); - } - return text; - }, - - /** * @param {Array} fileData * @returns {String} */ diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index b11b302c6c2..ad818d91413 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -153,6 +153,27 @@ // storage info like free space / used space }, + updateRow: function($tr, fileInfo, options) { + if(!fileInfo instanceof OCA.Sharing.SharedFileInfo) { + // recycle SharedFileInfo values if something tries to overwrite it + var oldModel = this.getModelForFile($tr); + + if(_.isUndefined(fileInfo.recipientData) && oldModel.recipientData) { + fileInfo.recipientData = oldModel.recipientData; + } + if(_.isUndefined(fileInfo.recipients) && oldModel.recipientData) { + fileInfo.recipientData = oldModel.recipientData; + } + if(_.isUndefined(fileInfo.shares) && oldModel.shares) { + fileInfo.shares = oldModel.shares; + } + if(_.isUndefined(fileInfo.shareOwner) && oldModel.shareOwner) { + fileInfo.shareOwner = oldModel.shareOwner; + } + } + OCA.Files.FileList.prototype._createRow.updateRow(this, arguments); + }, + reload: function() { this.showMask(); if (this._reloadCall) { @@ -225,7 +246,6 @@ }, _makeFilesFromRemoteShares: function(data) { - var self = this; var files = data; files = _.chain(files) @@ -297,6 +317,7 @@ }; if (self._sharedWithUser) { file.shareOwner = share.displayname_owner; + file.shareOwnerId = share.uid_owner; file.name = OC.basename(share.file_target); file.path = OC.dirname(share.file_target); file.permissions = share.permissions; @@ -307,6 +328,7 @@ else { if (share.share_type !== OC.Share.SHARE_TYPE_LINK) { file.share.targetDisplayName = share.share_with_displayname; + file.share.targetShareWithId = share.share_with; } file.name = OC.basename(share.path); file.path = OC.dirname(share.path); @@ -325,12 +347,14 @@ .reduce(function(memo, file) { var data = memo[file.id]; var recipient = file.share.targetDisplayName; + var recipientId = file.share.targetShareWithId; 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.recipients = {}; + data.recipientData = {}; // share types data.shareTypes = {}; // counter is cheaper than calling _.keys().length @@ -351,6 +375,10 @@ // only store the first ones, they will be the only ones // displayed data.recipients[recipient] = true; + data.recipientData[data.recipientsCount] = { + 'shareWith': recipientId, + 'shareWithDisplayName': recipient + }; } data.recipientsCount++; } @@ -367,11 +395,6 @@ // convert the recipients map to a flat // array of sorted names data.mountType = 'shared'; - data.recipients = _.keys(data.recipients); - data.recipientsDisplayName = OCA.Sharing.Util.formatRecipients( - data.recipients, - data.recipientsCount - ); delete data.recipientsCount; if (self._sharedWithUser) { // only for outgoing shres @@ -405,7 +428,16 @@ * @property {int} stime share timestamp in milliseconds * @property {String} [targetDisplayName] display name of the recipient * (only when shared with others) + * @property {String} [targetShareWithId] id of the recipient + * + */ + + /** + * Recipient attributes * + * @typedef {Object} OCA.Sharing.RecipientInfo + * @property {String} shareWith the id of the recipient + * @property {String} shareWithDisplayName the display name of the recipient */ /** @@ -419,7 +451,8 @@ * @property {String} shareOwner name of the share owner * @property {Array.<String>} recipients name of the first 4 recipients * (this is mostly for display purposes) - * @property {String} recipientsDisplayName display name + * @property {Object.<OCA.Sharing.RecipientInfo>} recipientData (as object for easier + * passing to HTML data attributes with jQuery) */ OCA.Sharing.FileList = FileList; diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index 5b0a78c9c64..893525f7566 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -140,6 +140,7 @@ describe('OCA.Sharing.Util tests', function() { size: 12, permissions: OC.PERMISSION_ALL, shareOwner: 'User One', + shareOwnerId: 'User One', etag: 'abc', shareTypes: [] }]); @@ -161,6 +162,16 @@ describe('OCA.Sharing.Util tests', function() { size: 12, permissions: OC.PERMISSION_ALL, recipientsDisplayName: 'User One, User Two', + recipientData: { + 0: { + shareWith: 'User One', + shareWithDisplayName: 'User One' + }, + 1: { + shareWith: 'User Two', + shareWithDisplayName: 'User Two' + } + }, etag: 'abc', shareTypes: [OC.Share.SHARE_TYPE_USER] }]); @@ -264,15 +275,13 @@ describe('OCA.Sharing.Util tests', function() { // simulate updating shares shareTab._dialog.model.set({ shares: [ - {share_with_displayname: 'User One'}, - {share_with_displayname: 'User Two'}, - {share_with_displayname: 'Group One'}, - {share_with_displayname: 'Group Two'} + {share_with_displayname: 'User One', share_with: 'User One'}, + {share_with_displayname: 'User Two', share_with: 'User Two'}, + {share_with_displayname: 'Group One', share_with: 'Group One'}, + {share_with_displayname: 'Group Two', share_with: 'Group Two'} ] }); - expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two'); - expect($action.text().trim()).toEqual('Shared with Group One Shared with Group Two Shared with User One Shared with User Two'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); @@ -298,14 +307,12 @@ describe('OCA.Sharing.Util tests', function() { // simulate updating shares shareTab._dialog.model.set({ shares: [ - {share_with_displayname: 'User One'}, - {share_with_displayname: 'User Two'}, - {share_with_displayname: 'User Three'} + {share_with_displayname: 'User One', share_with: 'User One'}, + {share_with_displayname: 'User Two', share_with: 'User Two'}, + {share_with_displayname: 'User Three', share_with: 'User Three'} ] }); - expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two'); - expect($action.text().trim()).toEqual('Shared with User One Shared with User Three Shared with User Two'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); @@ -334,7 +341,7 @@ describe('OCA.Sharing.Util tests', function() { shares: [] }); - expect($tr.attr('data-share-recipients')).not.toBeDefined(); + expect($tr.attr('data-share-recipient-data')).not.toBeDefined(); }); it('keep share text after updating reshare', function() { var $action, $tr; @@ -348,7 +355,8 @@ describe('OCA.Sharing.Util tests', function() { size: 12, permissions: OC.PERMISSION_ALL, etag: 'abc', - shareOwner: 'User One' + shareOwner: 'User One', + shareOwnerId: 'User One' }]); $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); @@ -360,8 +368,6 @@ describe('OCA.Sharing.Util tests', function() { shares: [{share_with_displayname: 'User Two'}] }); - expect($tr.attr('data-share-recipients')).toEqual('User Two'); - expect($action.find('>span').text().trim()).toEqual('Shared by User One'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); @@ -379,7 +385,9 @@ describe('OCA.Sharing.Util tests', function() { permissions: OC.PERMISSION_ALL, etag: 'abc', shareOwner: 'User One', - recipients: 'User Two' + shareOwnerId: 'User One', + recipients: 'User Two', + recipientData: {'User Two': 'User Two'} }]); $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); @@ -391,60 +399,13 @@ describe('OCA.Sharing.Util tests', function() { shares: [] }); - expect($tr.attr('data-share-recipients')).not.toBeDefined(); + expect($tr.attr('data-share-recipient-data')).not.toBeDefined(); expect($action.find('>span').text().trim()).toEqual('Shared by User One'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); }); }); - describe('formatRecipients', function() { - it('returns a single recipient when one passed', function() { - expect(OCA.Sharing.Util.formatRecipients(['User one'])) - .toEqual('User one'); - }); - it('returns two recipients when two passed', function() { - expect(OCA.Sharing.Util.formatRecipients(['User one', 'User two'])) - .toEqual('User one, User two'); - }); - it('returns four recipients with plus when five passed', function() { - var recipients = [ - 'User one', - 'User two', - 'User three', - 'User four', - 'User five' - ]; - expect(OCA.Sharing.Util.formatRecipients(recipients)) - .toEqual('User four, User one, User three, User two, +1'); - }); - it('returns four recipients with plus when ten passed', function() { - var recipients = [ - 'User one', - 'User two', - 'User three', - 'User four', - 'User five', - 'User six', - 'User seven', - 'User eight', - 'User nine', - 'User ten' - ]; - expect(OCA.Sharing.Util.formatRecipients(recipients)) - .toEqual('User four, User one, User three, User two, +6'); - }); - it('returns four recipients with plus when four passed with counter', function() { - var recipients = [ - 'User one', - 'User two', - 'User three', - 'User four' - ]; - expect(OCA.Sharing.Util.formatRecipients(recipients, 10)) - .toEqual('User four, User one, User three, User two, +6'); - }); - }); describe('Excluded lists', function() { function createListThenAttach(listId) { var fileActions = new OCA.Files.FileActions(); diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js index 3efbb8fcea3..903234947bd 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -628,7 +628,7 @@ describe('OCA.Sharing.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); // read and delete expect($tr.attr('data-mime')).toEqual('text/plain'); expect($tr.attr('data-mtime')).toEqual('11111000'); - expect($tr.attr('data-share-recipients')).not.toBeDefined(); + expect($tr.attr('data-share-recipient-data')).not.toBeDefined(); expect($tr.attr('data-share-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.attr('data-favorite')).toEqual('true'); @@ -681,7 +681,7 @@ describe('OCA.Sharing.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); // read and delete expect($tr.attr('data-mime')).toEqual('text/plain'); expect($tr.attr('data-mtime')).toEqual('11111000'); - expect($tr.attr('data-share-recipients')).not.toBeDefined(); + expect($tr.attr('data-share-recipient-data')).not.toBeDefined(); expect($tr.attr('data-share-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.attr('data-favorite')).toEqual('true'); diff --git a/autotest.sh b/autotest.sh index e7300363c3a..da9661f067b 100755 --- a/autotest.sh +++ b/autotest.sh @@ -308,11 +308,11 @@ function execute_tests { echo "Postgres is up." else if [ ! -z "$DRONE" ] ; then - DATABASEHOST=postgres + DATABASEHOST="postgres-$POSTGRES" fi echo "Waiting for Postgres to be available ..." if ! apps/files_external/tests/env/wait-for-connection $DATABASEHOST 5432 60; then - echo "[ERROR] Waited 60 seconds, no response" >&2 + echo "[ERROR] Waited 60 seconds for $DATABASEHOST, no response" >&2 exit 1 fi echo "Give it 10 additional seconds ..." diff --git a/core/js/share.js b/core/js/share.js index 25d59b46fb4..7662d6cffb9 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -161,7 +161,6 @@ OC.Share = _.extend(OC.Share || {}, { updateIcon:function(itemType, itemSource) { var shares = false; var link = false; - var image = OC.imagePath('core', 'actions/share'); var iconClass = ''; $.each(OC.Share.itemShares, function(index) { if (OC.Share.itemShares[index]) { @@ -200,15 +199,17 @@ OC.Share = _.extend(OC.Share || {}, { /** * Format a remote address * - * @param {String} remoteAddress full remote share + * @param {String} shareWith userid, full remote share, or whatever + * @param {String} shareWithDisplayName + * @param {String} message * @return {String} HTML code to display */ - _formatRemoteShare: function(remoteAddress, message) { - var parts = this._REMOTE_OWNER_REGEXP.exec(remoteAddress); + _formatRemoteShare: function(shareWith, shareWithDisplayName, message) { + var parts = this._REMOTE_OWNER_REGEXP.exec(shareWith); if (!parts) { // display avatar of the user - var avatar = '<span class="avatar" data-userName="' + escapeHTML(remoteAddress) + '" title="' + message + " " + escapeHTML(remoteAddress) + '"></span>'; - var hidden = '<span class="hidden-visually">' + message + ' ' + escapeHTML(remoteAddress) + '</span> '; + var avatar = '<span class="avatar" data-username="' + escapeHTML(shareWith) + '" title="' + message + " " + escapeHTML(shareWithDisplayName) + '"></span>'; + var hidden = '<span class="hidden-visually">' + message + ' ' + escapeHTML(shareWithDisplayName) + '</span> '; return avatar + hidden; } @@ -238,14 +239,17 @@ OC.Share = _.extend(OC.Share || {}, { * Loop over all recipients in the list and format them using * all kind of fancy magic. * - * @param {String[]} recipients array of all the recipients + * @param {Object} recipients array of all the recipients * @return {String[]} modified list of recipients */ _formatShareList: function(recipients) { var _parent = this; + recipients = _.toArray(recipients); + recipients.sort(function(a, b) { + return a.shareWithDisplayName.localeCompare(b.shareWithDisplayName); + }); return $.map(recipients, function(recipient) { - recipient = _parent._formatRemoteShare(recipient, t('core', 'Shared with')); - return recipient; + return _parent._formatRemoteShare(recipient.shareWith, recipient.shareWithDisplayName, t('core', 'Shared with')); }); }, /** @@ -261,12 +265,13 @@ OC.Share = _.extend(OC.Share || {}, { var type = $tr.data('type'); var icon = action.find('.icon'); var message, recipients, avatars; + var ownerId = $tr.attr('data-share-owner-id'); var owner = $tr.attr('data-share-owner'); var shareFolderIcon; var iconClass = 'icon-shared'; action.removeClass('shared-style'); // update folder icon - if (type === 'dir' && (hasShares || hasLink || owner)) { + if (type === 'dir' && (hasShares || hasLink || ownerId)) { if (hasLink) { shareFolderIcon = OC.MimeType.getIconUrl('dir-public'); } @@ -290,25 +295,26 @@ OC.Share = _.extend(OC.Share || {}, { $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); } // update share action text / icon - if (hasShares || owner) { - recipients = $tr.attr('data-share-recipients'); + if (hasShares || ownerId) { + recipients = $tr.data('share-recipient-data'); action.addClass('shared-style'); avatars = '<span>' + t('core', 'Shared') + '</span>'; // even if reshared, only show "Shared by" - if (owner) { + if (ownerId) { message = t('core', 'Shared by'); - avatars = this._formatRemoteShare(owner, message); + avatars = this._formatRemoteShare(ownerId, owner, message); } else if (recipients) { - avatars = this._formatShareList(recipients.split(', ')).join(''); + avatars = this._formatShareList(recipients); } action.html(avatars).prepend(icon); - if (owner || recipients) { + if (ownerId || recipients) { var avatarElement = action.find('.avatar'); - avatarElement.avatar(avatarElement.data('username'), 32); - - action.find('.icon-shared + span').tooltip({placement: 'top'}); + avatarElement.each(function () { + $(this).avatar($(this).data('username'), 32); + }); + action.find('span[title]').tooltip({placement: 'top'}); } } else { action.html('<span class="hidden-visually">' + t('core', 'Shared') + '</span>').prepend(icon); diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index 70c698c99a2..05057692e98 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -45,6 +45,7 @@ describe('OC.Share tests', function() { var $action; $file.attr('data-share-owner', input); + $file.attr('data-share-owner-id', input); OC.Share.markFileAsShared($file); $action = $file.find('.action-share>span').parent(); @@ -119,6 +120,7 @@ describe('OC.Share tests', function() { it('shows a shared folder icon for folders shared with the current user', function() { $file.attr('data-type', 'dir'); $file.attr('data-share-owner', 'someoneelse'); + $file.attr('data-share-owner-id', 'someoneelse'); OC.Share.markFileAsShared($file); checkIcon('filetypes/folder-shared'); @@ -155,7 +157,7 @@ describe('OC.Share tests', function() { function checkRecipients(input, output, title) { var $action; - $file.attr('data-share-recipients', input); + $file.attr('data-share-recipient-data', JSON.stringify(input)); OC.Share.markFileAsShared($file, true); $action = $file.find('.action-share>span').parent(); @@ -177,66 +179,187 @@ describe('OC.Share tests', function() { } it('displays the local share owner as is', function() { - checkRecipients('User One', 'Shared with User One', null); + var input = { + 0: { + shareWith: 'User One', + shareWithDisplayName: 'User One' + } + }; + checkRecipients(input, 'Shared with User One', null); }); it('displays the user name part of a remote recipient', function() { + var input = { + 0: { + shareWith: 'User One@someserver.com', + shareWithDisplayName: 'User One@someserver.com' + } + }; checkRecipients( - 'User One@someserver.com', + input, 'User One@…', 'Shared with User One@someserver.com' ); + + input = { + 0: { + shareWith: 'User One@someserver.com/', + shareWithDisplayName: 'User One@someserver.com/' + } + }; checkRecipients( - 'User One@someserver.com/', + input, 'User One@…', 'Shared with User One@someserver.com' ); + + input = { + 0: { + shareWith: 'User One@someserver.com/root/of/nextcloud', + shareWithDisplayName: 'User One@someserver.com/root/of/nextcloud' + } + }; checkRecipients( - 'User One@someserver.com/root/of/owncloud', + input, 'User One@…', 'Shared with User One@someserver.com' ); }); it('displays the user name part with domain of a remote share owner', function() { + var input = { + 0: { + shareWith: 'User One@example.com@someserver.com', + shareWithDisplayName: 'User One@example.com@someserver.com' + } + }; checkRecipients( - 'User One@example.com@someserver.com', + input, 'User One@example.com', 'Shared with User One@example.com@someserver.com' ); + + input = { + 0: { + shareWith: 'User One@example.com@someserver.com/', + shareWithDisplayName: 'User One@example.com@someserver.com/' + } + }; checkRecipients( - 'User One@example.com@someserver.com/', + input, 'User One@example.com', 'Shared with User One@example.com@someserver.com' ); + + input = { + 0: { + shareWith: 'User One@example.com@someserver.com/root/of/nextcloud', + shareWithDisplayName: 'User One@example.com@someserver.com/root/of/nextcloud' + } + }; checkRecipients( - 'User One@example.com@someserver.com/root/of/owncloud', + input, 'User One@example.com', 'Shared with User One@example.com@someserver.com' ); }); it('display multiple remote recipients', function() { + var input = { + 0: { + shareWith: 'One@someserver.com', + shareWithDisplayName: 'One@someserver.com' + }, + 1: { + shareWith: 'two@otherserver.com', + shareWithDisplayName: 'two@otherserver.com' + } + }; checkRecipients( - 'One@someserver.com, two@otherserver.com', + input, 'One@… two@…', ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); + + input = { + 0: { + shareWith: 'One@someserver.com/', + shareWithDisplayName: 'One@someserver.com/' + }, + 1: { + shareWith: 'two@someserver.com', + shareWithDisplayName: 'two@someserver.com' + } + }; checkRecipients( - 'One@someserver.com/, two@otherserver.com', + input, 'One@… two@…', ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); + + input = { + 0: { + shareWith: 'One@someserver.com/root/of/nextcloud', + shareWithDisplayName: 'One@someserver.com/root/of/nextcloud' + }, + 1: { + shareWith: 'two@someserver.com', + shareWithDisplayName: 'two@someserver.com' + } + }; checkRecipients( - 'One@someserver.com/root/of/owncloud, two@otherserver.com', + input, 'One@… two@…', ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); }); it('display mixed recipients', function() { checkRecipients( - 'One, two@otherserver.com', + { + 0: { + shareWith: 'One', + shareWithDisplayName: 'One' + }, + 1: { + shareWith: 'two@otherserver.com', + shareWithDisplayName: 'two@otherserver.com' + } + }, 'Shared with One two@…', ['Shared with two@otherserver.com'] ); }); + it('display multiple with divergent displaynames', function() { + var recipients = { + 0: { + shareWith: 'One', + shareWithDisplayName: 'Yoko Ono', + _output: 'Shared with Yoko Ono' + }, + 1: { + shareWith: 'two@otherserver.com', + shareWithDisplayName: 'two@othererver.com', + _output: 'two@…' + }, + 2: { + shareWith: 'Three', + shareWithDisplayName: 'Green, Mina', + _output: 'Shared with Green, Mina' + } + }; + + // we cannot assume the locale, also because PhantomJS has a bug. + var sortArray = _.toArray(recipients) + .sort(function(a, b) { + return a.shareWithDisplayName.localeCompare(b.shareWithDisplayName); + }); + var sortedOutput = _.map(sortArray, function(recipient) { + return recipient._output; + }).join(' '); + + checkRecipients( + recipients, + sortedOutput, + ['Shared with two@otherserver.com'] + ); + }); }); }); }); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 93c139b7bbe..81dad9890b5 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -540,7 +540,6 @@ return array( 'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => $baseDir . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\MySqlTools' => $baseDir . '/lib/private/DB/MySqlTools.php', - 'OC\\DB\\OCPostgreSqlPlatform' => $baseDir . '/lib/private/DB/OCPostgreSqlPlatform.php', 'OC\\DB\\OCSqlitePlatform' => $baseDir . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\OracleConnection' => $baseDir . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => $baseDir . '/lib/private/DB/OracleMigrator.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index c073d43ca14..b926100365b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -570,7 +570,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\MySqlTools' => __DIR__ . '/../../..' . '/lib/private/DB/MySqlTools.php', - 'OC\\DB\\OCPostgreSqlPlatform' => __DIR__ . '/../../..' . '/lib/private/DB/OCPostgreSqlPlatform.php', 'OC\\DB\\OCSqlitePlatform' => __DIR__ . '/../../..' . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\OracleConnection' => __DIR__ . '/../../..' . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/OracleMigrator.php', diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 082e81dfa74..fcb0117a0db 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -139,9 +139,6 @@ class ConnectionFactory { unset($additionalConnectionParams['host']); break; - case 'pgsql': - $additionalConnectionParams['platform'] = new OCPostgreSqlPlatform(); - break; case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; $additionalConnectionParams['platform'] = new OCSqlitePlatform(); diff --git a/lib/private/DB/OCPostgreSqlPlatform.php b/lib/private/DB/OCPostgreSqlPlatform.php deleted file mode 100644 index e66ab84252a..00000000000 --- a/lib/private/DB/OCPostgreSqlPlatform.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2017, ownCloud GmbH - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; -use Doctrine\DBAL\Schema\ColumnDiff; -use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\DBAL\Types\Type; - -class OCPostgreSqlPlatform extends PostgreSqlPlatform { - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff){ - $queries = parent::getAlterTableSQL($diff); - foreach ($queries as $index => $sql){ - // BIGSERIAL could not be used in statements altering column type - // That's why we replace it with BIGINT - // see https://github.com/owncloud/core/pull/28364#issuecomment-315006853 - if (preg_match('|(ALTER TABLE\s+\S+\s+ALTER\s+\S+\s+TYPE\s+)(BIGSERIAL)|i', $sql, $matches)) { - $alterTable = $matches[1]; - $queries[$index] = $alterTable . 'BIGINT'; - } - - // Changing integer to bigint kills next autoincrement value - // see https://github.com/owncloud/core/pull/28364#issuecomment-315006853 - if (preg_match('|ALTER TABLE\s+(\S+)\s+ALTER\s+(\S+)\s+DROP DEFAULT|i', $sql, $matches)) { - $queryColumnName = $matches[2]; - $columnDiff = $this->findColumnDiffByName($diff, $queryColumnName); - if ($columnDiff && $this->shouldSkipDropDefault($columnDiff)) { - unset($queries[$index]); - continue; - } - } - } - - return $queries; - } - - /** - * We should NOT drop next sequence value if - * - type was changed from INTEGER to BIGINT - * - column keeps an autoincrement - * - default value is kept NULL - * - * @param ColumnDiff $columnDiff - * @return bool - */ - private function shouldSkipDropDefault(ColumnDiff $columnDiff) { - $column = $columnDiff->column; - $fromColumn = $columnDiff->fromColumn; - return $fromColumn->getType()->getName() === Type::INTEGER - && $column->getType()->getName() === Type::BIGINT - && $fromColumn->getDefault() === null - && $column->getDefault() === null - && $fromColumn->getAutoincrement() - && $column->getAutoincrement(); - } - - /** - * @param TableDiff $diff - * @param string $name - * @return ColumnDiff | false - */ - private function findColumnDiffByName(TableDiff $diff, $name) { - foreach ($diff->changedColumns as $columnDiff) { - $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); - if ($oldColumnName === $name) { - return $columnDiff; - } - } - return false; - } -} diff --git a/tests/lib/DB/OCPostgreSqlPlatformTest.php b/tests/lib/DB/OCPostgreSqlPlatformTest.php index 56fab621cfc..54701bdcec9 100644 --- a/tests/lib/DB/OCPostgreSqlPlatformTest.php +++ b/tests/lib/DB/OCPostgreSqlPlatformTest.php @@ -18,33 +18,34 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ - + namespace Test\DB; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Type; -use OC\DB\OCPostgreSqlPlatform; - - /** + +/** * Class OCPostgreSqlPlatformTest * + * custom OCPostgreSqlPlatform behavior has been upstreamed, test is left to + * ensure behavior stays correct. + * * @group DB * * @package Test\DB */ - class OCPostgreSqlPlatformTest extends \Test\TestCase { - public function testAlterBigint(){ - $platform = new OCPostgreSqlPlatform(); + public function testAlterBigint() { + $platform = new PostgreSqlPlatform(); $sourceSchema = new Schema(); $targetSchema = new Schema(); - + $this->createTableAndColumn($sourceSchema, Type::INTEGER); $this->createTableAndColumn($targetSchema, Type::BIGINT); - + $comparator = new Comparator(); $diff = $comparator->compare($sourceSchema, $targetSchema); $sqlStatements = $diff->toSql($platform); @@ -53,22 +54,22 @@ class OCPostgreSqlPlatformTest extends \Test\TestCase { $sqlStatements, true ); - + $this->assertNotContains( 'ALTER TABLE poor_yorick ALTER id DROP DEFAULT', $sqlStatements, true ); } - - protected function createTableAndColumn($schema, $type){ + + protected function createTableAndColumn($schema, $type) { $table = $schema->createTable("poor_yorick"); $table->addColumn('id', $type, [ 'autoincrement' => true, - 'unsigned' => true, - 'notnull' => true, - 'length' => 11, + 'unsigned' => true, + 'notnull' => true, + 'length' => 11, ]); } - + } |