Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions apps/dav/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
['name' => 'invitation_response#decline', 'url' => '/invitation/decline/{token}', 'verb' => 'GET'],
['name' => 'invitation_response#options', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'GET'],
['name' => 'invitation_response#processMoreOptionsResult', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'POST'],
['name' => 'example_content#getDefaultContact', 'url' => '/api/defaultcontact/contact', 'verb' => 'GET'],
['name' => 'example_content#setDefaultContact', 'url' => '/api/defaultcontact/contact', 'verb' => 'PUT'],
['name' => 'example_content#setEnableDefaultContact', 'url' => '/api/defaultcontact/config', 'verb' => 'PUT'],
],
'ocs' => [
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
Expand Down
2 changes: 1 addition & 1 deletion apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
'OCA\\DAV\\ServerFactory' => $baseDir . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\AbsenceService' => $baseDir . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Service\\DefaultContactService' => $baseDir . '/../lib/Service/DefaultContactService.php',
'OCA\\DAV\\Service\\ExampleContactService' => $baseDir . '/../lib/Service/ExampleContactService.php',
'OCA\\DAV\\Service\\ExampleEventService' => $baseDir . '/../lib/Service/ExampleEventService.php',
'OCA\\DAV\\Settings\\Admin\\SystemAddressBookSettings' => $baseDir . '/../lib/Settings/Admin/SystemAddressBookSettings.php',
'OCA\\DAV\\Settings\\AvailabilitySettings' => $baseDir . '/../lib/Settings/AvailabilitySettings.php',
Expand Down
2 changes: 1 addition & 1 deletion apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
'OCA\\DAV\\ServerFactory' => __DIR__ . '/..' . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\AbsenceService' => __DIR__ . '/..' . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Service\\DefaultContactService' => __DIR__ . '/..' . '/../lib/Service/DefaultContactService.php',
'OCA\\DAV\\Service\\ExampleContactService' => __DIR__ . '/..' . '/../lib/Service/ExampleContactService.php',
'OCA\\DAV\\Service\\ExampleEventService' => __DIR__ . '/..' . '/../lib/Service/ExampleEventService.php',
'OCA\\DAV\\Settings\\Admin\\SystemAddressBookSettings' => __DIR__ . '/..' . '/../lib/Settings/Admin/SystemAddressBookSettings.php',
'OCA\\DAV\\Settings\\AvailabilitySettings' => __DIR__ . '/..' . '/../lib/Settings/AvailabilitySettings.php',
Expand Down
80 changes: 14 additions & 66 deletions apps/dav/lib/Controller/ExampleContentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,110 +10,58 @@
namespace OCA\DAV\Controller;

use OCA\DAV\AppInfo\Application;
use OCA\DAV\Service\ExampleContactService;
use OCA\DAV\Service\ExampleEventService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

class ExampleContentController extends ApiController {
private IAppData $appData;

public function __construct(
IRequest $request,
private IConfig $config,
private IAppConfig $appConfig,
private IAppDataFactory $appDataFactory,
private LoggerInterface $logger,
private ExampleEventService $exampleEventService,
private readonly LoggerInterface $logger,
private readonly ExampleEventService $exampleEventService,
private readonly ExampleContactService $exampleContactService,
) {
parent::__construct(Application::APP_ID, $request);
$this->appData = $this->appDataFactory->get('dav');
}

public function setEnableDefaultContact($allow) {
if ($allow === 'yes' && !$this->defaultContactExists()) {
#[FrontpageRoute(verb: 'PUT', url: '/api/defaultcontact/config')]
public function setEnableDefaultContact(bool $allow): JSONResponse {
if ($allow && !$this->exampleContactService->defaultContactExists()) {
try {
$this->setCard();
$this->exampleContactService->setCard();
} catch (\Exception $e) {
$this->logger->error('Could not create default contact', ['exception' => $e]);
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
$this->config->setAppValue(Application::APP_ID, 'enableDefaultContact', $allow);
$this->exampleContactService->setDefaultContactEnabled($allow);
return new JSONResponse([], Http::STATUS_OK);
}

#[NoCSRFRequired]
#[FrontpageRoute(verb: 'GET', url: '/api/defaultcontact/contact')]
public function getDefaultContact(): DataDownloadResponse {
$cardData = $this->getCard()
$cardData = $this->exampleContactService->getCard()
?? file_get_contents(__DIR__ . '/../ExampleContentFiles/exampleContact.vcf');
return new DataDownloadResponse($cardData, 'example_contact.vcf', 'text/vcard');
}

#[FrontpageRoute(verb: 'PUT', url: '/api/defaultcontact/contact')]
public function setDefaultContact(?string $contactData = null) {
if (!$this->config->getAppValue(Application::APP_ID, 'enableDefaultContact', 'yes')) {
if (!$this->exampleContactService->isDefaultContactEnabled()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
$this->setCard($contactData);
$this->exampleContactService->setCard($contactData);
return new JSONResponse([], Http::STATUS_OK);
}

private function getCard(): ?string {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
return null;
}

if (!$folder->fileExists('defaultContact.vcf')) {
return null;
}

return $folder->getFile('defaultContact.vcf')->getContent();
}

private function setCard(?string $cardData = null) {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder('defaultContact');
}

$isCustom = true;
if (is_null($cardData)) {
$cardData = file_get_contents(__DIR__ . '/../ExampleContentFiles/exampleContact.vcf');
$isCustom = false;
}

if (!$cardData) {
throw new \Exception('Could not read exampleContact.vcf');
}

$file = (!$folder->fileExists('defaultContact.vcf')) ? $folder->newFile('defaultContact.vcf') : $folder->getFile('defaultContact.vcf');
$file->putContent($cardData);

$this->appConfig->setValueBool(Application::APP_ID, 'hasCustomDefaultContact', $isCustom);
}

private function defaultContactExists(): bool {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
return false;
}
return $folder->fileExists('defaultContact.vcf');
}

#[FrontpageRoute(verb: 'POST', url: '/api/exampleEvent/enable')]
public function setCreateExampleEvent(bool $enable): JSONResponse {
$this->exampleEventService->setCreateExampleEvent($enable);
Expand Down
6 changes: 3 additions & 3 deletions apps/dav/lib/Listener/UserEventsListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\Service\DefaultContactService;
use OCA\DAV\Service\ExampleContactService;
use OCA\DAV\Service\ExampleEventService;
use OCP\Accounts\UserUpdatedEvent;
use OCP\Defaults;
Expand Down Expand Up @@ -46,7 +46,7 @@ public function __construct(
private CalDavBackend $calDav,
private CardDavBackend $cardDav,
private Defaults $themingDefaults,
private DefaultContactService $defaultContactService,
private ExampleContactService $exampleContactService,
private ExampleEventService $exampleEventService,
private LoggerInterface $logger,
) {
Expand Down Expand Up @@ -175,7 +175,7 @@ public function firstLogin(IUser $user): void {
}
}
if ($addressBookId) {
$this->defaultContactService->createDefaultContact($addressBookId);
$this->exampleContactService->createDefaultContact($addressBookId);
}
}
}
77 changes: 0 additions & 77 deletions apps/dav/lib/Service/DefaultContactService.php

This file was deleted.

132 changes: 132 additions & 0 deletions apps/dav/lib/Service/ExampleContactService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\Service;

use OCA\DAV\AppInfo\Application;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\AppFramework\Services\IAppConfig;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use Psr\Log\LoggerInterface;
use Symfony\Component\Uid\Uuid;

class ExampleContactService {
private readonly IAppData $appData;

public function __construct(
IAppDataFactory $appDataFactory,
private readonly IAppConfig $appConfig,
private readonly LoggerInterface $logger,
private readonly CardDavBackend $cardDav,
) {
$this->appData = $appDataFactory->get(Application::APP_ID);
}

public function isDefaultContactEnabled(): bool {
return $this->appConfig->getAppValueBool('enableDefaultContact', true);
}

public function setDefaultContactEnabled(bool $value): void {
$this->appConfig->setAppValueBool('enableDefaultContact', $value);
}

public function getCard(): ?string {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
return null;
}

if (!$folder->fileExists('defaultContact.vcf')) {
return null;
}

return $folder->getFile('defaultContact.vcf')->getContent();
}

public function setCard(?string $cardData = null) {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder('defaultContact');
}

$isCustom = true;
if (is_null($cardData)) {
$cardData = file_get_contents(__DIR__ . '/../ExampleContentFiles/exampleContact.vcf');
$isCustom = false;
}

if (!$cardData) {
throw new \Exception('Could not read exampleContact.vcf');
}

$file = (!$folder->fileExists('defaultContact.vcf')) ? $folder->newFile('defaultContact.vcf') : $folder->getFile('defaultContact.vcf');
$file->putContent($cardData);

$this->appConfig->setAppValueBool('hasCustomDefaultContact', $isCustom);
}

public function defaultContactExists(): bool {
try {
$folder = $this->appData->getFolder('defaultContact');
} catch (NotFoundException $e) {
return false;
}
return $folder->fileExists('defaultContact.vcf');
}

public function createDefaultContact(int $addressBookId): void {
if (!$this->isDefaultContactEnabled()) {
return;
}

try {
$folder = $this->appData->getFolder('defaultContact');
$defaultContactFile = $folder->getFile('defaultContact.vcf');
$data = $defaultContactFile->getContent();
} catch (\Exception $e) {
$this->logger->error('Couldn\'t get default contact file', ['exception' => $e]);
return;
}

// Make sure the UID is unique
$newUid = Uuid::v4()->toRfc4122();
$newRev = date('Ymd\THis\Z');
$vcard = \Sabre\VObject\Reader::read($data, \Sabre\VObject\Reader::OPTION_FORGIVING);
if ($vcard->UID) {
$vcard->UID->setValue($newUid);
} else {
$vcard->add('UID', $newUid);
}
if ($vcard->REV) {
$vcard->REV->setValue($newRev);
} else {
$vcard->add('REV', $newRev);
}

// Level 3 means that the document is invalid
// https://sabre.io/vobject/vcard/#validating-vcard
$level3Warnings = array_filter($vcard->validate(), static function ($warning) {
return $warning['level'] === 3;
});

if (!empty($level3Warnings)) {
$this->logger->error('Default contact is invalid', ['warnings' => $level3Warnings]);
return;
}
try {
$this->cardDav->createCard($addressBookId, 'default', $vcard->serialize(), false);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
}
}
}
Loading
Loading