diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/comments/css/comments.css | 51 | ||||
-rw-r--r-- | apps/comments/js/commentstabview.js | 43 | ||||
-rw-r--r-- | apps/dav/appinfo/v1/caldav.php | 3 | ||||
-rw-r--r-- | apps/dav/lib/CalDAV/CalDavBackend.php | 29 | ||||
-rw-r--r-- | apps/dav/lib/Command/CreateCalendar.php | 3 | ||||
-rw-r--r-- | apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php | 7 | ||||
-rw-r--r-- | apps/dav/lib/RootCollection.php | 3 | ||||
-rw-r--r-- | apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php | 6 | ||||
-rw-r--r-- | apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php | 5 | ||||
-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/templates/public.php | 6 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/shareSpec.js | 89 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/sharedfilelistSpec.js | 4 | ||||
-rw-r--r-- | apps/updatenotification/lib/Notification/BackgroundJob.php | 9 | ||||
-rw-r--r-- | apps/updatenotification/tests/Notification/BackgroundJobTest.php | 8 | ||||
-rw-r--r-- | apps/user_ldap/templates/settings.php | 2 |
18 files changed, 222 insertions, 148 deletions
diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index 6b0452da1fd..646f5f1100c 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -42,6 +42,20 @@ #commentsTabView .newCommentForm .submitLoading { background-position: left; + + /* Match rules for '#commentsTabView .newCommentForm .submit' to place the + loading icon at the same position as the confirm icon */ + position: absolute; + bottom: 0px; + right: 8px; + width: 30px; + margin: 0; + padding: 7px 9px; + + /* Match rules for 'input[type="submit"]' to place the loading icon at the + same position as the confirm icon */ + min-height: 34px; + box-sizing: border-box; } #commentsTabView .newCommentForm .cancel { @@ -69,6 +83,7 @@ width: 32px; height: 32px; line-height: 32px; + margin-right: 5px; } #commentsTabView .comment .message .avatar, @@ -112,7 +127,7 @@ background-repeat: no-repeat; } -#commentsTabView .authorRow>div { +#commentsTabView .authorRow>div:not(.contactsmenu-popover) { display: inline-block; vertical-align: middle; } @@ -125,7 +140,25 @@ .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; + width: 100%; +} + +#commentsTabView .comments li .message .atwho-inserted { + margin-left: 5px; } .atwho-view-ul * .avatar-name-wrapper { @@ -136,23 +169,20 @@ #commentsTabView .comment .date { opacity: .5; } -#commentsTabView .comment .author { - margin-left: 5px; -} #commentsTabView .comment .date { - position: absolute; - right: 0; - top: 5px; + margin-left: auto; } #commentsTabView .comments li .message { padding-left: 40px; + display: inline-flex; + flex-wrap: wrap; + align-items: center; } #commentsTabView .comment .action { opacity: 0; - vertical-align: middle; - display: inline-block; + padding: 5px; } #commentsTabView .comment:hover .action { @@ -163,7 +193,8 @@ opacity: 1; } -#commentsTabView .comment .action.delete { +#commentsTabView .comment .action.delete, +#commentsTabView .comment .deleteLoading { position: absolute; right: 0; } diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 0d2d0b0b81f..0c43e156985 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -23,10 +23,11 @@ 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>'+ '{{/if}}' + ' </div>' + ' <form class="newCommentForm">' + @@ -42,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}}' + @@ -214,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 ); }); }, @@ -320,7 +323,7 @@ this.$container.append($comment); } this._postRenderItem($comment); - $('#commentsTabView').find('.newCommentForm div.message').text('').prop('disabled', false); + $('#commentsTabView').find('.newCommentForm div.message').text('').prop('contenteditable', true); // we need to update the model, because it consists of client data // only, but the server might add meta data, e.g. about mentions @@ -377,7 +380,7 @@ }); }, - _postRenderItem: function($el) { + _postRenderItem: function($el, editionMode) { $el.find('.has-tooltip').tooltip(); $el.find('.avatar').each(function() { var $this = $(this); @@ -395,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); + } }); }, @@ -445,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>'; @@ -486,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')); @@ -543,7 +556,7 @@ ev.preventDefault(); var $comment = $(ev.target).closest('.comment'); var commentId = $comment.data('id'); - var $loading = $comment.find('.submitLoading'); + var $loading = $comment.find('.deleteLoading'); var $commentField = $comment.find('.message'); var $submit = $comment.find('.submit'); var $cancel = $comment.find('.cancel'); @@ -631,7 +644,7 @@ return; } - $commentField.prop('disabled', true); + $commentField.prop('contenteditable', false); $submit.addClass('hidden'); $loading.removeClass('hidden'); @@ -684,7 +697,7 @@ _onSubmitError: function($form, commentId) { $form.find('.submit').removeClass('hidden'); $form.find('.submitLoading').addClass('hidden'); - $form.find('.message').prop('disabled', false); + $form.find('.message').prop('contenteditable', true); if(!_.isUndefined(commentId)) { OC.Notification.show(t('comments', 'Error occurred while updating comment with id {id}', {id: commentId}), {type: 'error'}); diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php index e96c3f28064..d9851bf92c8 100644 --- a/apps/dav/appinfo/v1/caldav.php +++ b/apps/dav/appinfo/v1/caldav.php @@ -49,8 +49,9 @@ $principalBackend = new Principal( $db = \OC::$server->getDatabaseConnection(); $userManager = \OC::$server->getUserManager(); $random = \OC::$server->getSecureRandom(); +$logger = \OC::$server->getLogger(); $dispatcher = \OC::$server->getEventDispatcher(); -$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, \OC::$server->getGroupManager(), $random, $dispatcher, true); +$calDavBackend = new CalDavBackend($db, $principalBackend, $userManager, \OC::$server->getGroupManager(), $random, $logger, $dispatcher, true); $debugging = \OC::$server->getConfig()->getSystemValue('debug', false); $sendInvitations = \OC::$server->getConfig()->getAppValue('dav', 'sendInvitations', 'yes') === 'yes'; diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 9045a62cde4..2f591a262a2 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -37,6 +37,7 @@ use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; use OCP\IDBConnection; use OCP\IGroupManager; +use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; @@ -56,6 +57,8 @@ use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VEvent; use Sabre\VObject\Component\VTimeZone; use Sabre\VObject\DateTimeParser; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\ParseException; use Sabre\VObject\Property; use Sabre\VObject\Reader; use Sabre\VObject\Recur\EventIterator; @@ -152,6 +155,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription /** @var ISecureRandom */ private $random; + /** @var ILogger */ + private $logger; + /** @var EventDispatcherInterface */ private $dispatcher; @@ -169,6 +175,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param IUserManager $userManager * @param IGroupManager $groupManager * @param ISecureRandom $random + * @param ILogger $logger * @param EventDispatcherInterface $dispatcher * @param bool $legacyEndpoint */ @@ -177,6 +184,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription IUserManager $userManager, IGroupManager $groupManager, ISecureRandom $random, + ILogger $logger, EventDispatcherInterface $dispatcher, $legacyEndpoint = false) { $this->db = $db; @@ -184,6 +192,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->userManager = $userManager; $this->sharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'calendar'); $this->random = $random; + $this->logger = $logger; $this->dispatcher = $dispatcher; $this->legacyEndpoint = $legacyEndpoint; } @@ -1219,7 +1228,25 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($requirePostFilter) { - if (!$this->validateFilterForObject($row, $filters)) { + // validateFilterForObject will parse the calendar data + // catch parsing errors + try { + $matches = $this->validateFilterForObject($row, $filters); + } catch(ParseException $ex) { + $this->logger->logException($ex, [ + 'app' => 'dav', + 'message' => 'Caught parsing exception for calendar data. This usually indicates invalid calendar data. calendar-id:'.$calendarId.' uri:'.$row['uri'] + ]); + continue; + } catch (InvalidDataException $ex) { + $this->logger->logException($ex, [ + 'app' => 'dav', + 'message' => 'Caught invalid data exception for calendar data. This usually indicates invalid calendar data. calendar-id:'.$calendarId.' uri:'.$row['uri'] + ]); + continue; + } + + if (!$matches) { continue; } } diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php index 1ef859e0631..190e4aa339f 100644 --- a/apps/dav/lib/Command/CreateCalendar.php +++ b/apps/dav/lib/Command/CreateCalendar.php @@ -78,10 +78,11 @@ class CreateCalendar extends Command { $this->groupManager ); $random = \OC::$server->getSecureRandom(); + $logger = \OC::$server->getLogger(); $dispatcher = \OC::$server->getEventDispatcher(); $name = $input->getArgument('name'); - $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $this->groupManager, $random, $dispatcher); + $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $this->groupManager, $random, $logger, $dispatcher); $caldav->createCalendar("principals/users/$user", $name, []); } } diff --git a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php index 5721f483832..68c9a1b415f 100644 --- a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php +++ b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php @@ -30,8 +30,10 @@ use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; use OCP\Files\StorageNotAvailableException; use OCP\ILogger; 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; @@ -41,6 +43,8 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { // If tokenauth can throw this exception (which is basically as // NotAuthenticated. So not fatal. PasswordLoginForbidden::class => true, + // basically a NotAuthenticated + InvalidSyncToken::class => true, // the sync client uses this to find out whether files exist, // so it is not always an error, log it as debug NotFound::class => true, @@ -54,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/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index e4ba1f2c02a..7af1745cd74 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -40,6 +40,7 @@ class RootCollection extends SimpleCollection { public function __construct() { $config = \OC::$server->getConfig(); $random = \OC::$server->getSecureRandom(); + $logger = \OC::$server->getLogger(); $userManager = \OC::$server->getUserManager(); $groupManager = \OC::$server->getGroupManager(); $db = \OC::$server->getDatabaseConnection(); @@ -61,7 +62,7 @@ class RootCollection extends SimpleCollection { $systemPrincipals->disableListing = $disableListing; $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); $filesCollection->disableListing = $disableListing; - $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $dispatcher); + $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher); $calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users'); $calendarRoot->disableListing = $disableListing; $publicCalendarRoot = new PublicCalendarRoot($caldavBackend); diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php index 90bf860b24d..2a01bd425c0 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php @@ -28,6 +28,7 @@ namespace OCA\DAV\Tests\unit\CalDAV; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\Connector\Sabre\Principal; use OCP\IGroupManager; +use OCP\ILogger; use OCP\IUserManager; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; @@ -57,6 +58,8 @@ abstract class AbstractCalDavBackend extends TestCase { /** @var ISecureRandom */ private $random; + /** @var ILogger */ + private $logger; const UNIT_TEST_USER = 'principals/users/caldav-unit-test'; const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1'; @@ -84,7 +87,8 @@ abstract class AbstractCalDavBackend extends TestCase { $db = \OC::$server->getDatabaseConnection(); $this->random = \OC::$server->getSecureRandom(); - $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->groupManager, $this->random, $this->dispatcher); + $this->logger = $this->createMock(ILogger::class); + $this->backend = new CalDavBackend($db, $this->principal, $this->userManager, $this->groupManager, $this->random, $this->logger, $this->dispatcher); $this->cleanUpBackend(); } diff --git a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php index 82f4161c206..57707c6c0a4 100644 --- a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php +++ b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php @@ -34,6 +34,7 @@ use OCP\IGroupManager; use OCP\IL10N; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\PublicCalendarRoot; +use OCP\ILogger; use OCP\IUserManager; use OCP\Security\ISecureRandom; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -64,6 +65,8 @@ class PublicCalendarRootTest extends TestCase { /** @var ISecureRandom */ private $random; + /** @var ILogger */ + private $logger; public function setUp() { parent::setUp(); @@ -73,6 +76,7 @@ class PublicCalendarRootTest extends TestCase { $this->userManager = $this->createMock(IUserManager::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->random = \OC::$server->getSecureRandom(); + $this->logger = $this->createMock(ILogger::class); $dispatcher = $this->createMock(EventDispatcherInterface::class); $this->principal->expects($this->any())->method('getGroupMembership') @@ -85,6 +89,7 @@ class PublicCalendarRootTest extends TestCase { $this->userManager, $this->groupManager, $this->random, + $this->logger, $dispatcher ); 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/templates/public.php b/apps/files_sharing/templates/public.php index 5417809b908..e17595d548b 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -97,6 +97,12 @@ $maxUploadFilesize = min($upload_max_filesize, $post_max_size); <source src="<?php p($_['downloadURL']); ?>" type="<?php p($_['mimetype']); ?>" /> </video> </div> + <?php elseif ($_['previewEnabled'] && substr($_['mimetype'], 0, strpos($_['mimetype'], '/')) == 'audio'): ?> + <div id="imgframe"> + <audio tabindex="0" controls="" preload="none" style="width: 100%; max-width: <?php p($_['previewMaxX']); ?>px; max-height: <?php p($_['previewMaxY']); ?>px"> + <source src="<?php p($_['downloadURL']); ?>" type="<?php p($_['mimetype']); ?>" /> + </audio> + </div> <?php else: ?> <!-- Preview frame is filled via JS to support SVG images for modern browsers --> <div id="imgframe"></div> 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/apps/updatenotification/lib/Notification/BackgroundJob.php b/apps/updatenotification/lib/Notification/BackgroundJob.php index 08baa35664f..3c0cac60cde 100644 --- a/apps/updatenotification/lib/Notification/BackgroundJob.php +++ b/apps/updatenotification/lib/Notification/BackgroundJob.php @@ -53,6 +53,9 @@ class BackgroundJob extends TimedJob { /** @var IClientService */ protected $client; + /** @var Installer */ + protected $installer; + /** @var string[] */ protected $users; @@ -64,8 +67,9 @@ class BackgroundJob extends TimedJob { * @param IGroupManager $groupManager * @param IAppManager $appManager * @param IClientService $client + * @param Installer $installer */ - public function __construct(IConfig $config, IManager $notificationManager, IGroupManager $groupManager, IAppManager $appManager, IClientService $client) { + public function __construct(IConfig $config, IManager $notificationManager, IGroupManager $groupManager, IAppManager $appManager, IClientService $client, Installer $installer) { // Run once a day $this->setInterval(60 * 60 * 24); @@ -74,6 +78,7 @@ class BackgroundJob extends TimedJob { $this->groupManager = $groupManager; $this->appManager = $appManager; $this->client = $client; + $this->installer = $installer; } protected function run($argument) { @@ -257,6 +262,6 @@ class BackgroundJob extends TimedJob { * @return string|false */ protected function isUpdateAvailable($app) { - return Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher()); + return $this->installer->isUpdateAvailable($app); } } diff --git a/apps/updatenotification/tests/Notification/BackgroundJobTest.php b/apps/updatenotification/tests/Notification/BackgroundJobTest.php index 92a8a687f5a..0355b10a09c 100644 --- a/apps/updatenotification/tests/Notification/BackgroundJobTest.php +++ b/apps/updatenotification/tests/Notification/BackgroundJobTest.php @@ -23,6 +23,7 @@ namespace OCA\UpdateNotification\Tests\Notification; +use OC\Installer; use OCA\UpdateNotification\Notification\BackgroundJob; use OCP\App\IAppManager; use OCP\Http\Client\IClientService; @@ -47,6 +48,8 @@ class BackgroundJobTest extends TestCase { protected $appManager; /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ protected $client; + /** @var Installer|\PHPUnit_Framework_MockObject_MockObject */ + protected $installer; public function setUp() { parent::setUp(); @@ -56,6 +59,7 @@ class BackgroundJobTest extends TestCase { $this->groupManager = $this->createMock(IGroupManager::class); $this->appManager = $this->createMock(IAppManager::class); $this->client = $this->createMock(IClientService::class); + $this->installer = $this->createMock(Installer::class); } /** @@ -69,7 +73,8 @@ class BackgroundJobTest extends TestCase { $this->notificationManager, $this->groupManager, $this->appManager, - $this->client + $this->client, + $this->installer ); } { return $this->getMockBuilder(BackgroundJob::class) @@ -79,6 +84,7 @@ class BackgroundJobTest extends TestCase { $this->groupManager, $this->appManager, $this->client, + $this->installer, ]) ->setMethods($methods) ->getMock(); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 82e61cd5dcd..42bbaaf78ca 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -55,7 +55,7 @@ style('user_ldap', 'settings'); ?> <form id="ldap" class="section" action="#" method="post"> - <h2><?php p($l->t('LDAP')); ?></h2> + <h2><?php p($l->t('LDAP / AD integration')); ?></h2> <div id="ldapSettings"> <ul> |