Skip to content

Commit

Permalink
feat(dav): expose Nextcloud groups to Contact's system addressbook co…
Browse files Browse the repository at this point in the history
…ntacts

Closes #38432

Signed-off-by: Thomas Citharel <[email protected]>
  • Loading branch information
tcitworld committed Oct 19, 2023
1 parent 694352e commit d5e1af8
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 26 deletions.
21 changes: 20 additions & 1 deletion apps/dav/lib/CardDAV/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
namespace OCA\DAV\CardDAV;

use Exception;
use OCA\DAV\AppInfo\Application;
use OCP\Accounts\IAccountManager;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IImage;
use OCP\IUser;
Expand All @@ -42,12 +46,16 @@ class Converter {
/** @var IAccountManager */
private $accountManager;
private IUserManager $userManager;
private IGroupManager $groupManager;
private IConfig $config;

public function __construct(IAccountManager $accountManager,
IUserManager $userManager, IURLGenerator $urlGenerator) {
IUserManager $userManager, IGroupManager $groupManager, IURLGenerator $urlGenerator, IConfig $config) {
$this->accountManager = $accountManager;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->urlGenerator = $urlGenerator;
$this->config = $config;
}

public function createCardFromUser(IUser $user): ?VCard {
Expand Down Expand Up @@ -151,6 +159,17 @@ public function createCardFromUser(IUser $user): ?VCard {
}
}

if ($this->config->getAppValue(Application::APP_ID, 'system_addressbook_expose_groups', 'yes') === 'yes') {
$groupsToIgnore = explode(',', $this->config->getAppValue(Application::APP_ID, 'system_addressbook_groups_to_ignore', ''));
$groupNames = array_reduce($this->groupManager->getUserGroups($user), function ($groupNames, IGroup $group) use ($groupsToIgnore) {
if (!in_array($group->getGID(), $groupsToIgnore, true)) {
$groupNames[] = $group->getDisplayName();
}
return $groupNames;
}, []);
$vCard->add(new Text($vCard, 'CATEGORIES', implode(',', $groupNames)));
}

if ($publish && !empty($cloudId)) {
$vCard->add(new Text($vCard, 'CLOUD', $cloudId));
$vCard->validate();
Expand Down
89 changes: 64 additions & 25 deletions apps/dav/tests/unit/CardDAV/ConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,45 @@
*/
namespace OCA\DAV\Tests\unit\CardDAV;

use OCA\DAV\AppInfo\Application;
use OCA\DAV\CardDAV\Converter;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IImage;
use OCP\IUser;
use OCP\IUserManager;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;
use Test\TestCase;

class ConverterTest extends TestCase {

/** @var IAccountManager|\PHPUnit\Framework\MockObject\MockObject */
private $accountManager;
/** @var IUserManager|(IUserManager&MockObject)|MockObject */
private IAccountManager|MockObject $accountManager;
private IUserManager|MockObject $userManager;
private IGroupManager|MockObject $groupManager;
private IURLGenerator|MockObject $urlGenerator;

/** @var IURLGenerator */
private $urlGenerator;
private IConfig|MockObject $config;

protected function setUp(): void {
parent::setUp();

$this->accountManager = $this->createMock(IAccountManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->config = $this->createMock(IConfig::class);
}

/**
* @return IAccountProperty|MockObject
*/
protected function getAccountPropertyMock(string $name, ?string $value, string $scope) {
protected function getAccountPropertyMock(string $name, ?string $value, string $scope): IAccountProperty|MockObject {
$property = $this->createMock(IAccountProperty::class);
$property->expects($this->any())
->method('getName')
Expand Down Expand Up @@ -108,10 +114,10 @@ public function testCreation($expectedVCard, $displayName = null, $eMailAddress
$user = $this->getUserMock((string)$displayName, $eMailAddress, $cloudId);
$accountManager = $this->getAccountManager($user);

$converter = new Converter($accountManager, $this->userManager, $this->urlGenerator);
$converter = new Converter($accountManager, $this->userManager, $this->groupManager, $this->urlGenerator, $this->config);
$vCard = $converter->createCardFromUser($user);
if ($expectedVCard !== null) {
$this->assertInstanceOf('Sabre\VObject\Component\VCard', $vCard);
$this->assertInstanceOf(VCard::class, $vCard);
$cardData = $vCard->jsonSerialize();
$this->compareData($expectedVCard, $cardData);
} else {
Expand All @@ -129,7 +135,7 @@ public function testManagerProp(): void {
->willReturn('Manager');
$accountManager = $this->getAccountManager($user);

$converter = new Converter($accountManager, $this->userManager, $this->urlGenerator);
$converter = new Converter($accountManager, $this->userManager, $this->groupManager, $this->urlGenerator, $this->config);
$vCard = $converter->createCardFromUser($user);

$this->compareData(
Expand All @@ -142,7 +148,7 @@ public function testManagerProp(): void {
);
}

protected function compareData($expected, $data) {
protected function compareData($expected, $data): void {
foreach ($expected as $key => $value) {
$found = false;
foreach ($data[1] as $d) {
Expand All @@ -152,12 +158,12 @@ protected function compareData($expected, $data) {
}
}
if (!$found) {
$this->assertTrue(false, 'Expected data: ' . $key . ' not found.');
$this->fail('Expected data: ' . $key . ' not found.');
}
}
}

public function providesNewUsers() {
public function providesNewUsers(): array {
return [
[
null
Expand Down Expand Up @@ -213,31 +219,23 @@ public function providesNewUsers() {

/**
* @dataProvider providesNames
* @param $expected
* @param $fullName
*/
public function testNameSplitter($expected, $fullName): void {
$converter = new Converter($this->accountManager, $this->userManager, $this->urlGenerator);
public function testNameSplitter(string $expected, string $fullName): void {
$converter = new Converter($this->accountManager, $this->userManager, $this->groupManager, $this->urlGenerator, $this->config);
$r = $converter->splitFullName($fullName);
$r = implode(';', $r);
$this->assertEquals($expected, $r);
}

public function providesNames() {
public function providesNames(): array {
return [
['Sauron;;;;', 'Sauron'],
['Baggins;Bilbo;;;', 'Bilbo Baggins'],
['Tolkien;John;Ronald Reuel;;', 'John Ronald Reuel Tolkien'],
];
}

/**
* @param $displayName
* @param $eMailAddress
* @param $cloudId
* @return IUser | \PHPUnit\Framework\MockObject\MockObject
*/
protected function getUserMock(string $displayName, ?string $eMailAddress, ?string $cloudId) {
protected function getUserMock(string $displayName, ?string $eMailAddress, ?string $cloudId): IUser|MockObject {
$image0 = $this->getMockBuilder(IImage::class)->disableOriginalConstructor()->getMock();
$image0->method('mimeType')->willReturn('image/jpeg');
$image0->method('data')->willReturn('123456789');
Expand All @@ -249,4 +247,45 @@ protected function getUserMock(string $displayName, ?string $eMailAddress, ?stri
$user->method('getAvatarImage')->willReturn($image0);
return $user;
}

/**
* @dataProvider dataForTestExposeGroups
*/
public function testExposeGroups(bool $exposeGroups, array $groups, string $groupsToIgnore, ?string $result): void {
$user = $this->getUserMock("user", "[email protected]", "[email protected]");
$this->config->expects($exposeGroups ? $this->exactly(2) :$this->once())->method('getAppValue')->withConsecutive(
[Application::APP_ID, 'system_addressbook_expose_groups', 'yes'],
[Application::APP_ID, 'system_addressbook_groups_to_ignore', '']
)->willReturnOnConsecutiveCalls(
$exposeGroups ? 'yes' : 'no',
$groupsToIgnore
);

$ignoredGroups = explode(',', $groupsToIgnore);

$this->groupManager->expects($exposeGroups ? $this->once() : $this->never())->method('getUserGroups')->with($user)->willReturn(array_map(function ($groupName) use ($groups, $exposeGroups, $ignoredGroups) {
$group = $this->createMock(IGroup::class);
$group->expects($exposeGroups ? $this->once() : $this->never())->method('getGID')->willReturn($groupName);
$group->expects($exposeGroups && !in_array($groupName, $ignoredGroups, true) ? $this->once() : $this->never())->method('getDisplayName')->willReturn($groupName);
return $group;
}, $groups));
$accountManager = $this->getAccountManager($user);

$converter = new Converter($accountManager, $this->userManager, $this->groupManager, $this->urlGenerator, $this->config);
$vCard = $converter->createCardFromUser($user);

$parsed = Reader::read($vCard->serialize(), Reader::OPTION_FORGIVING);

$this->assertEquals((string) $parsed->CATEGORIES, $result);
}

public function dataForTestExposeGroups(): array {
return [
[false, ['myGroup1'], '', null],
[true, ['myGroup1'], '', 'myGroup1'],
[true, ['myGroup1', 'myGroup2'], '', 'myGroup1,myGroup2'],
[true, ['myGroup1', 'myGroup2', 'myGroup3', 'myGroup4'], 'myGroup2,myGroup3', 'myGroup1,myGroup4'],
[true, ['myGroup1', 'myGroup2'], 'myGroup2,myGroup3', 'myGroup1']
];
}
}

0 comments on commit d5e1af8

Please sign in to comment.