Pārlūkot izejas kodu

Show sharing recommendations

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
tags/v16.0.0alpha1
Christoph Wurst pirms 5 gadiem
vecāks
revīzija
f3023aaa85
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam

+ 2
- 2
apps/accessibility/js/accessibility.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
apps/accessibility/js/accessibility.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 5
- 0
apps/files_sharing/appinfo/routes.php Parādīt failu

@@ -94,6 +94,11 @@ return [
'url' => '/api/v1/sharees',
'verb' => 'GET',
],
[
'name' => 'ShareesAPI#findRecommended',
'url' => '/api/v1/sharees_recommended',
'verb' => 'GET',
],
/*
* Remote Shares
*/

+ 157
- 1
apps/files_sharing/lib/Controller/ShareesAPIController.php Parādīt failu

@@ -29,17 +29,29 @@ declare(strict_types=1);
*/
namespace OCA\Files_Sharing\Controller;

use function array_filter;
use function array_slice;
use function array_values;
use Generator;
use OC\Collaboration\Collaborators\SearchResult;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCSController;
use OCP\Collaboration\Collaborators\ISearch;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
use OCP\IRequest;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\Share;
use OCP\Share\IManager;
use function usort;

class ShareesAPIController extends OCSController {

/** @var userId */
protected $userId;

/** @var IConfig */
protected $config;

@@ -87,6 +99,7 @@ class ShareesAPIController extends OCSController {
private $collaboratorSearch;

/**
* @param string $UserId
* @param string $appName
* @param IRequest $request
* @param IConfig $config
@@ -95,6 +108,7 @@ class ShareesAPIController extends OCSController {
* @param ISearch $collaboratorSearch
*/
public function __construct(
$UserId,
string $appName,
IRequest $request,
IConfig $config,
@@ -103,7 +117,7 @@ class ShareesAPIController extends OCSController {
ISearch $collaboratorSearch
) {
parent::__construct($appName, $request);
$this->userId = $UserId;
$this->config = $config;
$this->urlGenerator = $urlGenerator;
$this->shareManager = $shareManager;
@@ -212,6 +226,148 @@ class ShareesAPIController extends OCSController {
return $response;
}

/**
* @param string $user
* @param int $shareType
*
* @return Generator<array<string>>
*/
private function getAllShareesByType(string $user, int $shareType): Generator {
$offset = 0;
$pageSize = 50;

while (count($page = $this->shareManager->getSharesBy(
$user,
$shareType,
null,
false,
$pageSize,
$offset
))) {
foreach ($page as $share) {
yield [$share->getSharedWith(), $share->getSharedWithDisplayName() ?? $share->getSharedWith()];
}

$offset += $pageSize;
}
}

private function sortShareesByFrequency(array $sharees): array {
usort($sharees, function(array $s1, array $s2) {
return $s2['count'] - $s1['count'];
});
return $sharees;
}

private $searchResultTypeMap = [
Share::SHARE_TYPE_USER => 'users',
Share::SHARE_TYPE_GROUP => 'groups',
Share::SHARE_TYPE_REMOTE => 'remotes',
Share::SHARE_TYPE_REMOTE_GROUP => 'remote_groups',
Share::SHARE_TYPE_EMAIL => 'emails',
];

private function getAllSharees(string $user, array $shareTypes): ISearchResult {
$result = [];
foreach ($shareTypes as $shareType) {
$sharees = $this->getAllShareesByType($user, $shareType);
$shareTypeResults = [];
foreach ($sharees as list($sharee, $displayname)) {
if (!isset($this->searchResultTypeMap[$shareType])) {
continue;
}

if (!isset($shareTypeResults[$sharee])) {
$shareTypeResults[$sharee] = [
'count' => 1,
'label' => $displayname,
'value' => [
'shareType' => $shareType,
'shareWith' => $sharee,
],
];
} else {
$shareTypeResults[$sharee]['count']++;
}
}
$result = array_merge($result, array_values($shareTypeResults));
}

$top5 = array_slice(
$this->sortShareesByFrequency($result),
0,
5
);

$searchResult = new SearchResult();
foreach ($this->searchResultTypeMap as $int => $str) {
$searchResult->addResultSet(new SearchResultType($str), [], []);
foreach ($top5 as $x) {
if ($x['value']['shareType'] === $int) {
$searchResult->addResultSet(new SearchResultType($str), [], [$x]);
}
}
}
return $searchResult;
}

/**
* @NoAdminRequired
*
* @param string $itemType
* @return DataResponse
* @throws OCSBadRequestException
*/
public function findRecommended(string $itemType = null, $shareType = null): DataResponse {
$shareTypes = [
Share::SHARE_TYPE_USER,
];

if ($itemType === null) {
throw new OCSBadRequestException('Missing itemType');
} elseif ($itemType === 'file' || $itemType === 'folder') {
if ($this->shareManager->allowGroupSharing()) {
$shareTypes[] = Share::SHARE_TYPE_GROUP;
}

if ($this->isRemoteSharingAllowed($itemType)) {
$shareTypes[] = Share::SHARE_TYPE_REMOTE;
}

if ($this->isRemoteGroupSharingAllowed($itemType)) {
$shareTypes[] = Share::SHARE_TYPE_REMOTE_GROUP;
}

if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
$shareTypes[] = Share::SHARE_TYPE_EMAIL;
}

if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_ROOM)) {
$shareTypes[] = Share::SHARE_TYPE_ROOM;
}
} else {
$shareTypes[] = Share::SHARE_TYPE_GROUP;
$shareTypes[] = Share::SHARE_TYPE_EMAIL;
}

// FIXME: DI
if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
$shareTypes[] = Share::SHARE_TYPE_CIRCLE;
}

if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
sort($shareTypes);
} else if (is_numeric($shareType)) {
$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
sort($shareTypes);
}

return new DataResponse(
$this->getAllSharees($this->userId, $shareTypes)->asArray()
);
}

/**
* Method to get out the static call for better testing
*

+ 11
- 0
apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php Parādīt failu

@@ -50,6 +50,9 @@ class ShareesAPIControllerTest extends TestCase {
/** @var ShareesAPIController */
protected $sharees;

/** @var string */
protected $uid;

/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
protected $request;

@@ -62,6 +65,7 @@ class ShareesAPIControllerTest extends TestCase {
protected function setUp() {
parent::setUp();

$this->uid = 'test123';
$this->request = $this->createMock(IRequest::class);
$this->shareManager = $this->createMock(IManager::class);

@@ -74,6 +78,7 @@ class ShareesAPIControllerTest extends TestCase {
$this->collaboratorSearch = $this->createMock(ISearch::class);

$this->sharees = new ShareesAPIController(
$this->uid,
'files_sharing',
$this->request,
$configMock,
@@ -243,6 +248,8 @@ class ShareesAPIControllerTest extends TestCase {
->method('allowGroupSharing')
->willReturn($allowGroupSharing);

/** @var string */
$uid = 'test123';
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject $request */
$request = $this->createMock(IRequest::class);
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject $urlGenerator */
@@ -251,6 +258,7 @@ class ShareesAPIControllerTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Files_Sharing\Controller\ShareesAPIController $sharees */
$sharees = $this->getMockBuilder('\OCA\Files_Sharing\Controller\ShareesAPIController')
->setConstructorArgs([
$uid,
'files_sharing',
$request,
$config,
@@ -335,6 +343,8 @@ class ShareesAPIControllerTest extends TestCase {
$config->expects($this->never())
->method('getAppValue');

/** @var string */
$uid = 'test123';
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject $request */
$request = $this->createMock(IRequest::class);
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject $urlGenerator */
@@ -343,6 +353,7 @@ class ShareesAPIControllerTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Files_Sharing\Controller\ShareesAPIController $sharees */
$sharees = $this->getMockBuilder('\OCA\Files_Sharing\Controller\ShareesAPIController')
->setConstructorArgs([
$uid,
'files_sharing',
$request,
$config,

+ 5
- 11
apps/twofactor_backupcodes/js/settings.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
apps/twofactor_backupcodes/js/settings.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 551
- 55
apps/updatenotification/js/updatenotification.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
apps/updatenotification/js/updatenotification.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
core/js/dist/share_backend.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
core/js/dist/share_backend.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 296
- 1
core/js/sharedialogview.js Parādīt failu

@@ -50,6 +50,9 @@
/** @type {object} **/
_lastSuggestions: undefined,

/** @type {object} **/
_lastRecommendations: undefined,

/** @type {int} **/
_pendingOperationsCount: 0,

@@ -382,7 +385,299 @@
return this._lastSuggestions.promise;
},

_getRecommendations: function(model) {
if (this._lastRecommendations &&
this._lastRecommendations.model === model) {
return this._lastRecommendations.promise;
}

var deferred = $.Deferred();

$.get(
OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees_recommended',
{
format: 'json',
itemType: model.get('itemType')
},
function (result) {
if (result.ocs.meta.statuscode === 100) {
var filter = function(users, groups, remotes, remote_groups, emails, circles, rooms) {
if (typeof(emails) === 'undefined') {
emails = [];
}
if (typeof(circles) === 'undefined') {
circles = [];
}
if (typeof(rooms) === 'undefined') {
rooms = [];
}

var usersLength;
var groupsLength;
var remotesLength;
var remoteGroupsLength;
var emailsLength;
var circlesLength;
var roomsLength;

var i, j;

//Filter out the current user
usersLength = users.length;
for (i = 0; i < usersLength; i++) {
if (users[i].value.shareWith === OC.currentUser) {
users.splice(i, 1);
break;
}
}

// Filter out the owner of the share
if (model.hasReshare()) {
usersLength = users.length;
for (i = 0 ; i < usersLength; i++) {
if (users[i].value.shareWith === model.getReshareOwner()) {
users.splice(i, 1);
break;
}
}
}

var shares = model.get('shares');
var sharesLength = shares.length;

// Now filter out all sharees that are already shared with
for (i = 0; i < sharesLength; i++) {
var share = shares[i];

if (share.share_type === OC.Share.SHARE_TYPE_USER) {
usersLength = users.length;
for (j = 0; j < usersLength; j++) {
if (users[j].value.shareWith === share.share_with) {
users.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_GROUP) {
groupsLength = groups.length;
for (j = 0; j < groupsLength; j++) {
if (groups[j].value.shareWith === share.share_with) {
groups.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) {
remotesLength = remotes.length;
for (j = 0; j < remotesLength; j++) {
if (remotes[j].value.shareWith === share.share_with) {
remotes.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE_GROUP) {
remoteGroupsLength = remote_groups.length;
for (j = 0; j < remoteGroupsLength; j++) {
if (remote_groups[j].value.shareWith === share.share_with) {
remote_groups.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) {
emailsLength = emails.length;
for (j = 0; j < emailsLength; j++) {
if (emails[j].value.shareWith === share.share_with) {
emails.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_CIRCLE) {
circlesLength = circles.length;
for (j = 0; j < circlesLength; j++) {
if (circles[j].value.shareWith === share.share_with) {
circles.splice(j, 1);
break;
}
}
} else if (share.share_type === OC.Share.SHARE_TYPE_ROOM) {
roomsLength = rooms.length;
for (j = 0; j < roomsLength; j++) {
if (rooms[j].value.shareWith === share.share_with) {
rooms.splice(j, 1);
break;
}
}
}
}
};

filter(
result.ocs.data.exact.users,
result.ocs.data.exact.groups,
result.ocs.data.exact.remotes,
result.ocs.data.exact.remote_groups,
result.ocs.data.exact.emails,
result.ocs.data.exact.circles,
result.ocs.data.exact.rooms
);

var exactUsers = result.ocs.data.exact.users;
var exactGroups = result.ocs.data.exact.groups;
var exactRemotes = result.ocs.data.exact.remotes || [];
var exactRemoteGroups = result.ocs.data.exact.remote_groups || [];
var exactEmails = [];
if (typeof(result.ocs.data.emails) !== 'undefined') {
exactEmails = result.ocs.data.exact.emails;
}
var exactCircles = [];
if (typeof(result.ocs.data.circles) !== 'undefined') {
exactCircles = result.ocs.data.exact.circles;
}
var exactRooms = [];
if (typeof(result.ocs.data.rooms) !== 'undefined') {
exactRooms = result.ocs.data.exact.rooms;
}

var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactRemoteGroups).concat(exactEmails).concat(exactCircles).concat(exactRooms);

filter(
result.ocs.data.users,
result.ocs.data.groups,
result.ocs.data.remotes,
result.ocs.data.remote_groups,
result.ocs.data.emails,
result.ocs.data.circles,
result.ocs.data.rooms
);

var users = result.ocs.data.users;
var groups = result.ocs.data.groups;
var remotes = result.ocs.data.remotes || [];
var remoteGroups = result.ocs.data.remote_groups || [];
var lookup = result.ocs.data.lookup || [];
var emails = [];
if (typeof(result.ocs.data.emails) !== 'undefined') {
emails = result.ocs.data.emails;
}
var circles = [];
if (typeof(result.ocs.data.circles) !== 'undefined') {
circles = result.ocs.data.circles;
}
var rooms = [];
if (typeof(result.ocs.data.rooms) !== 'undefined') {
rooms = result.ocs.data.rooms;
}

var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup);

function dynamicSort(property) {
return function (a,b) {
var aProperty = '';
var bProperty = '';
if (typeof a[property] !== 'undefined') {
aProperty = a[property];
}
if (typeof b[property] !== 'undefined') {
bProperty = b[property];
}
return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0;
}
}

/**
* Sort share entries by uuid to properly group them
*/
var grouped = suggestions.sort(dynamicSort('uuid'));

var previousUuid = null;
var groupedLength = grouped.length;
var result = [];
/**
* build the result array that only contains all contact entries from
* merged contacts, if the search term matches its contact name
*/
for (var i = 0; i < groupedLength; i++) {
if (typeof grouped[i].uuid !== 'undefined' && grouped[i].uuid === previousUuid) {
grouped[i].merged = true;
}
if (typeof grouped[i].merged === 'undefined') {
result.push(grouped[i]);
}
previousUuid = grouped[i].uuid;
}
var moreResultsAvailable =
(
oc_config['sharing.maxAutocompleteResults'] > 0
&& Math.min(perPage, oc_config['sharing.maxAutocompleteResults'])
<= Math.max(
users.length + exactUsers.length,
groups.length + exactGroups.length,
remoteGroups.length + exactRemoteGroups.length,
remotes.length + exactRemotes.length,
emails.length + exactEmails.length,
circles.length + exactCircles.length,
rooms.length + exactRooms.length,
lookup.length
)
);

deferred.resolve(result, exactMatches, moreResultsAvailable);
} else {
deferred.reject(result.ocs.meta.message);
}
}
).fail(function() {
deferred.reject();
});

this._lastRecommendations = {
model: model,
promise: deferred.promise()
};

return this._lastRecommendations.promise;
},

recommendationHandler: function (response) {
var view = this;
var $shareWithField = $('.shareWithField');
this._getRecommendations(
view.model
).done(function(suggestions, exactMatches) {
view._pendingOperationsCount--;
if (view._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$loading.removeClass('inlineblock');
$confirm.removeClass('hidden');
}

if (suggestions.length > 0) {
$shareWithField
.autocomplete("option", "autoFocus", true);

response(suggestions);
} else {
console.info('no sharing recommendations found');
response();
}
}).fail(function(message) {
view._pendingOperationsCount--;
if (view._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$loading.removeClass('inlineblock');
$confirm.removeClass('hidden');
}

console.error('could not load recommendations', message)
});
},

autocompleteHandler: function (search, response) {
// If nothing is entered we show recommendations instead of search
// results
if (search.term.length === 0) {
this.recommendationHandler(response);
return;
}

var $shareWithField = $('.shareWithField'),
view = this,
$loading = this.$el.find('.shareWithLoading'),
@@ -766,7 +1061,7 @@
};

$shareField.autocomplete({
minLength: 1,
minLength: 0,
delay: 750,
focus: function(event) {
event.preventDefault();

+ 12
- 52
settings/js/vue-0.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-0.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-3.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-4.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-4.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-5.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-5.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-6.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-6.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 15
- 55
settings/js/vue-settings-admin-security.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-settings-admin-security.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 5
- 5
settings/js/vue-settings-apps-users-management.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 1
- 1
settings/js/vue-settings-apps-users-management.js.map
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


Notiek ielāde…
Atcelt
Saglabāt