summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/Controller/AutoCompleteController.php102
-rw-r--r--core/Controller/WalledGardenController.php42
-rw-r--r--core/css/guest.css10
-rw-r--r--core/css/icons.scss2
-rw-r--r--core/css/inputs.scss8
-rw-r--r--core/js/share.js39
-rw-r--r--core/js/tests/specs/shareSpec.js63
-rw-r--r--core/routes.php2
-rw-r--r--core/templates/loginflow/authpicker.php4
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>