diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/Controller/AutoCompleteController.php | 102 | ||||
-rw-r--r-- | core/Controller/WalledGardenController.php | 42 | ||||
-rw-r--r-- | core/css/guest.css | 10 | ||||
-rw-r--r-- | core/css/icons.scss | 2 | ||||
-rw-r--r-- | core/css/inputs.scss | 8 | ||||
-rw-r--r-- | core/js/share.js | 39 | ||||
-rw-r--r-- | core/js/tests/specs/shareSpec.js | 63 | ||||
-rw-r--r-- | core/routes.php | 2 | ||||
-rw-r--r-- | core/templates/loginflow/authpicker.php | 4 |
9 files changed, 211 insertions, 61 deletions
diff --git a/core/Controller/AutoCompleteController.php b/core/Controller/AutoCompleteController.php new file mode 100644 index 00000000000..2e01f85c639 --- /dev/null +++ b/core/Controller/AutoCompleteController.php @@ -0,0 +1,102 @@ +<?php +/** + * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Core\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; +use OCP\Collaboration\AutoComplete\IManager; +use OCP\Collaboration\Collaborators\ISearch; +use OCP\IConfig; +use OCP\IRequest; +use OCP\Share; + +class AutoCompleteController extends Controller { + /** @var ISearch */ + private $collaboratorSearch; + /** @var IManager */ + private $autoCompleteManager; + /** @var IConfig */ + private $config; + + public function __construct( + $appName, + IRequest $request, + ISearch $collaboratorSearch, + IManager $autoCompleteManager, + IConfig $config + ) { + parent::__construct($appName, $request); + + $this->collaboratorSearch = $collaboratorSearch; + $this->autoCompleteManager = $autoCompleteManager; + $this->config = $config; + } + + /** + * @NoAdminRequired + * + * @param string $search + * @param string $itemType + * @param string $itemId + * @param string|null $sorter can be piped, top prio first, e.g.: "commenters|share-recipients" + * @param array $shareTypes + * @param int $limit + * @return DataResponse + */ + public function get($search, $itemType, $itemId, $sorter = null, $shareTypes = [Share::SHARE_TYPE_USER], $limit = 10) { + // if enumeration/user listings are disabled, we'll receive an empty + // result from search() – thus nothing else to do here. + list($results,) = $this->collaboratorSearch->search($search, $shareTypes, false, $limit, 0); + + $exactMatches = $results['exact']; + unset($results['exact']); + $results = array_merge_recursive($exactMatches, $results); + + $sorters = array_reverse(explode('|', $sorter)); + $this->autoCompleteManager->runSorters($sorters, $results, [ + 'itemType' => $itemType, + 'itemId' => $itemId, + ]); + + // transform to expected format + $results = $this->prepareResultArray($results); + + return new DataResponse($results); + } + + + protected function prepareResultArray(array $results) { + $output = []; + foreach ($results as $type => $subResult) { + foreach ($subResult as $result) { + $output[] = [ + 'id' => $result['value']['shareWith'], + 'label' => $result['label'], + 'source' => $type, + ]; + } + } + return $output; + } +} diff --git a/core/Controller/WalledGardenController.php b/core/Controller/WalledGardenController.php new file mode 100644 index 00000000000..737f5396779 --- /dev/null +++ b/core/Controller/WalledGardenController.php @@ -0,0 +1,42 @@ +<?php +/** + * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Core\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Response; + +class WalledGardenController extends Controller { + + /** + * @PublicPage + * @NoCSRFRequired + * + * @return Response + */ + function get() { + $resp = new Response(); + $resp->setStatus(Http::STATUS_NO_CONTENT); + return $resp; + } +} diff --git a/core/css/guest.css b/core/css/guest.css index ee6d44d98e8..576efafec5f 100644 --- a/core/css/guest.css +++ b/core/css/guest.css @@ -152,7 +152,7 @@ form #datadirField legend { applied to the button instead. */ } -input, textarea, select, button { +input, textarea, select, button, div[contenteditable=true] { font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; } input { @@ -664,16 +664,16 @@ p.info { margin: -9px 0 0 -9px; } /* Css replaced elements don't have ::after nor ::before */ -img.icon-loading, object.icon-loading, video.icon-loading, button.icon-loading, textarea.icon-loading, input.icon-loading, select.icon-loading { +img.icon-loading, object.icon-loading, video.icon-loading, button.icon-loading, textarea.icon-loading, input.icon-loading, select.icon-loading, div[contenteditable=true].icon-loading { background-image: url('../img/loading.gif'); } -img.icon-loading-dark, object.icon-loading-dark, video.icon-loading-dark, button.icon-loading-dark, textarea.icon-loading-dark, input.icon-loading-dark, select.icon-loading-dark { +img.icon-loading-dark, object.icon-loading-dark, video.icon-loading-dark, button.icon-loading-dark, textarea.icon-loading-dark, input.icon-loading-dark, select.icon-loading-dark, div[contenteditable=true].icon-loading-dark { background-image: url('../img/loading-dark.gif'); } -img.icon-loading-small, object.icon-loading-small, video.icon-loading-small, button.icon-loading-small, textarea.icon-loading-small, input.icon-loading-small, select.icon-loading-small { +img.icon-loading-small, object.icon-loading-small, video.icon-loading-small, button.icon-loading-small, textarea.icon-loading-small, input.icon-loading-small, select.icon-loading-small, div[contenteditable=true].icon-loading-small { background-image: url('../img/loading-small.gif'); } -img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading-small-dark, button.icon-loading-small-dark, textarea.icon-loading-small-dark, input.icon-loading-small-dark, select.icon-loading-small-dark { +img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading-small-dark, button.icon-loading-small-dark, textarea.icon-loading-small-dark, input.icon-loading-small-dark, select.icon-loading-small-dark, div[contenteditable=true].icon-loading-small-dark { background-image: url('../img/loading-small-dark.gif'); } @-webkit-keyframes rotate { diff --git a/core/css/icons.scss b/core/css/icons.scss index 36c550575de..bdef106e3cd 100644 --- a/core/css/icons.scss +++ b/core/css/icons.scss @@ -61,7 +61,7 @@ } /* Css replaced elements don't have ::after nor ::before */ -img, object, video, button, textarea, input, select { +img, object, video, button, textarea, input, select, div[contenteditable=true] { .icon-loading { background-image: url('../img/loading.gif'); } diff --git a/core/css/inputs.scss b/core/css/inputs.scss index eb7d20cf8af..5143ec518ed 100644 --- a/core/css/inputs.scss +++ b/core/css/inputs.scss @@ -13,7 +13,7 @@ */ /* Specifically override browser styles */ -input, textarea, select, button { +input, textarea, select, button, div[contenteditable=true] { font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; } .select2-container-multi .select2-choices .select2-search-field input, .select2-search input, .ui-widget { @@ -24,7 +24,8 @@ input, textarea, select, button { select, button, input, -textarea { +textarea, +div[contenteditable=true] { width: 130px; min-height: 32px; box-sizing: border-box; @@ -35,6 +36,7 @@ select, button, .button, input:not([type='range']), textarea, +div[contenteditable=true], .pager li a { margin: 3px 3px 3px 0; padding: 7px 6px; @@ -154,7 +156,7 @@ button, .button { } } -textarea { +textarea, div[contenteditable=true] { color: nc-lighten($color-main-text, 33%); cursor: text; font-family: inherit; diff --git a/core/js/share.js b/core/js/share.js index 659d719788d..25d59b46fb4 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -203,18 +203,19 @@ OC.Share = _.extend(OC.Share || {}, { * @param {String} remoteAddress full remote share * @return {String} HTML code to display */ - _formatRemoteShare: function(remoteAddress) { + _formatRemoteShare: function(remoteAddress, message) { var parts = this._REMOTE_OWNER_REGEXP.exec(remoteAddress); if (!parts) { - // display as is, most likely to be a simple owner name - return escapeHTML(remoteAddress); + // 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> '; + return avatar + hidden; } var userName = parts[1]; var userDomain = parts[3]; var server = parts[4]; - var dir = parts[6]; - var tooltip = userName; + var tooltip = message + ' ' + userName; if (userDomain) { tooltip += '@' + userDomain; } @@ -230,7 +231,7 @@ OC.Share = _.extend(OC.Share || {}, { if (userDomain) { html += '<span class="userDomain">@' + escapeHTML(userDomain) + '</span>'; } - html += '</span>'; + html += '</span> '; return html; }, /** @@ -243,7 +244,7 @@ OC.Share = _.extend(OC.Share || {}, { _formatShareList: function(recipients) { var _parent = this; return $.map(recipients, function(recipient) { - recipient = _parent._formatRemoteShare(recipient); + recipient = _parent._formatRemoteShare(recipient, t('core', 'Shared with')); return recipient; }); }, @@ -259,8 +260,7 @@ OC.Share = _.extend(OC.Share || {}, { var action = $tr.find('.fileactions .action[data-action="Share"]'); var type = $tr.data('type'); var icon = action.find('.icon'); - var message; - var recipients; + var message, recipients, avatars; var owner = $tr.attr('data-share-owner'); var shareFolderIcon; var iconClass = 'icon-shared'; @@ -294,20 +294,23 @@ OC.Share = _.extend(OC.Share || {}, { recipients = $tr.attr('data-share-recipients'); action.addClass('shared-style'); - message = t('core', 'Shared'); + avatars = '<span>' + t('core', 'Shared') + '</span>'; // even if reshared, only show "Shared by" if (owner) { - message = this._formatRemoteShare(owner); + message = t('core', 'Shared by'); + avatars = this._formatRemoteShare(owner, message); + } else if (recipients) { + avatars = this._formatShareList(recipients.split(', ')).join(''); } - else if (recipients) { - message = t('core', 'Shared with {recipients}', {recipients: this._formatShareList(recipients.split(", ")).join(", ")}, 0, {escape: false}); - } - action.html('<span> ' + message + '</span>').prepend(icon); + action.html(avatars).prepend(icon); + if (owner || recipients) { - action.find('.remoteAddress').tooltip({placement: 'top'}); + var avatarElement = action.find('.avatar'); + avatarElement.avatar(avatarElement.data('username'), 32); + + action.find('.icon-shared + span').tooltip({placement: 'top'}); } - } - else { + } else { action.html('<span class="hidden-visually">' + t('core', 'Shared') + '</span>').prepend(icon); } if (hasLink) { diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index fbf6eecc8df..70c698c99a2 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -47,7 +47,7 @@ describe('OC.Share tests', function() { $file.attr('data-share-owner', input); OC.Share.markFileAsShared($file); - $action = $file.find('.action-share>span'); + $action = $file.find('.action-share>span').parent(); expect($action.text().trim()).toEqual(output); if (_.isString(title)) { expect($action.find('.remoteAddress').attr('title')).toEqual(title); @@ -58,41 +58,41 @@ describe('OC.Share tests', function() { tooltipStub.reset(); } - it('displays the local share owner as is', function() { - checkOwner('User One', 'User One', null); + it('displays the local share owner with "Shared by" prefix', function() { + checkOwner('User One', 'Shared by User One', null); }); it('displays the user name part of a remote share owner', function() { checkOwner( 'User One@someserver.com', 'User One@…', - 'User One@someserver.com' + 'Shared by User One@someserver.com' ); checkOwner( 'User One@someserver.com/', 'User One@…', - 'User One@someserver.com' + 'Shared by User One@someserver.com' ); checkOwner( 'User One@someserver.com/root/of/owncloud', 'User One@…', - 'User One@someserver.com' + 'Shared by User One@someserver.com' ); }); it('displays the user name part with domain of a remote share owner', function() { checkOwner( 'User One@example.com@someserver.com', 'User One@example.com', - 'User One@example.com@someserver.com' + 'Shared by User One@example.com@someserver.com' ); checkOwner( 'User One@example.com@someserver.com/', 'User One@example.com', - 'User One@example.com@someserver.com' + 'Shared by User One@example.com@someserver.com' ); checkOwner( 'User One@example.com@someserver.com/root/of/owncloud', 'User One@example.com', - 'User One@example.com@someserver.com' + 'Shared by User One@example.com@someserver.com' ); }); }); @@ -151,14 +151,14 @@ describe('OC.Share tests', function() { }); }); - describe('displaying the recipoients', function() { + describe('displaying the recipients', function() { function checkRecipients(input, output, title) { var $action; $file.attr('data-share-recipients', input); OC.Share.markFileAsShared($file, true); - $action = $file.find('.action-share>span'); + $action = $file.find('.action-share>span').parent(); expect($action.text().trim()).toEqual(output); if (_.isString(title)) { expect($action.find('.remoteAddress').attr('title')).toEqual(title); @@ -182,62 +182,61 @@ describe('OC.Share tests', function() { it('displays the user name part of a remote recipient', function() { checkRecipients( 'User One@someserver.com', - 'Shared with User One@…', - 'User One@someserver.com' + 'User One@…', + 'Shared with User One@someserver.com' ); checkRecipients( 'User One@someserver.com/', - 'Shared with User One@…', - 'User One@someserver.com' + 'User One@…', + 'Shared with User One@someserver.com' ); checkRecipients( 'User One@someserver.com/root/of/owncloud', - 'Shared with User One@…', - 'User One@someserver.com' + 'User One@…', + 'Shared with User One@someserver.com' ); }); it('displays the user name part with domain of a remote share owner', function() { checkRecipients( 'User One@example.com@someserver.com', - 'Shared with User One@example.com', - 'User One@example.com@someserver.com' + 'User One@example.com', + 'Shared with User One@example.com@someserver.com' ); checkRecipients( 'User One@example.com@someserver.com/', - 'Shared with User One@example.com', - 'User One@example.com@someserver.com' + 'User One@example.com', + 'Shared with User One@example.com@someserver.com' ); checkRecipients( 'User One@example.com@someserver.com/root/of/owncloud', - 'Shared with User One@example.com', - 'User One@example.com@someserver.com' + 'User One@example.com', + 'Shared with User One@example.com@someserver.com' ); }); it('display multiple remote recipients', function() { checkRecipients( 'One@someserver.com, two@otherserver.com', - 'Shared with One@…, two@…', - ['One@someserver.com', 'two@otherserver.com'] + 'One@… two@…', + ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); checkRecipients( 'One@someserver.com/, two@otherserver.com', - 'Shared with One@…, two@…', - ['One@someserver.com', 'two@otherserver.com'] + 'One@… two@…', + ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); checkRecipients( 'One@someserver.com/root/of/owncloud, two@otherserver.com', - 'Shared with One@…, two@…', - ['One@someserver.com', 'two@otherserver.com'] + 'One@… two@…', + ['Shared with One@someserver.com', 'Shared with two@otherserver.com'] ); }); it('display mixed recipients', function() { checkRecipients( 'One, two@otherserver.com', - 'Shared with One, two@…', - ['two@otherserver.com'] + 'Shared with One two@…', + ['Shared with two@otherserver.com'] ); }); }); }); }); - diff --git a/core/routes.php b/core/routes.php index af445d9da8f..bca62098b94 100644 --- a/core/routes.php +++ b/core/routes.php @@ -62,6 +62,8 @@ $application->registerRoutes($this, [ ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], + ['name' => 'AutoComplete#get', 'url' => 'autocomplete/get', 'verb' => 'GET'], + ['name' => 'WalledGarden#get', 'url' => '/204', 'verb' => 'GET'], ], 'ocs' => [ ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], diff --git a/core/templates/loginflow/authpicker.php b/core/templates/loginflow/authpicker.php index 810c32b4f09..1858f8bcb62 100644 --- a/core/templates/loginflow/authpicker.php +++ b/core/templates/loginflow/authpicker.php @@ -40,7 +40,7 @@ $urlGenerator = $_['urlGenerator']; <p id="redirect-link"> <a href="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.redirectPage', ['stateToken' => $_['stateToken'], 'clientIdentifier' => $_['clientIdentifier'], 'oauthState' => $_['oauthState']])) ?>"> - <input type="submit" class="login primary icon-confirm-white" value="<?php p('Grant access') ?>"> + <input type="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Grant access')) ?>"> </a> </p> @@ -54,7 +54,7 @@ $urlGenerator = $_['urlGenerator']; <label for="password" class="infield"><?php p($l->t('Password')) ?></label> </p> <input type="hidden" id="serverHost" value="<?php p($_['serverHost']) ?>" /> - <input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p('Grant access') ?>"> + <input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Grant access')) ?>"> </fieldset> </div> |